MyBatisPlus 常用知识点总结

完整的Mybatis-Plus项目#

  • 创建User类
Copy
package com.example.ssmpdemo.entity; import com.baomidou.mybatisplus.annotation.*; import com.example.ssmpdemo.enums.Status; import lombok.Data; import java.util.Date; /** * 使用@TableName定义时,忽略已定义的table前缀 * @author wangchao */ @Data @TableName(value = "tb_book2") public class Book { @TableId(type = IdType.AUTO) private Integer id; private String type; private String name; private String description; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; @TableField(fill = FieldFill.INSERT) private Date createTime; /** * 直接使用自定义的枚举类型 */ private Status status; /** * 定义乐观锁 */ @Version private Integer version; }
  • 创建UserMapper

BaseMapper<T> : Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能

Copy
package com.example.ssmpdemo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.ssmpdemo.entity.Book; /** * 继承自BaseMapper后,不需要再编写常见的增删改查方法。 * @author wangchao */ public interface BookMapper extends BaseMapper<Book> { }
  • 在启动类上添加@MapperScan(value = "com.example.ssmpdemo.mapper")或在对应的mapper类上添加@Mapper
Copy
package com.example.ssmpdemo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @MapperScan(value = "com.example.ssmpdemo.mapper") @SpringBootApplication public class SsmpdemoApplication { public static void main(String[] args) { SpringApplication.run(SsmpdemoApplication.class, args); } }

service接口和实现类:

Copy
// 定义接口 package com.example.ssmpdemo.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.ssmpdemo.entity.Book; /** * 使用IService自动补全接口需要的函数,避免重复开发。 * @author wangchao */ public interface IBookService extends IService<Book> { } // 定义实现类 package com.example.ssmpdemo.service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.ssmpdemo.entity.Book; import com.example.ssmpdemo.mapper.BookMapper; import org.springframework.stereotype.Service; /** * 使用ServiceImpl自动实现Service * @author wangchao */ @Service public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService { }
  • 编写application.yml
Copy
spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:5506/ssmpdemo?serverTimezone=UTC username: root password: xxxxx mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: # 添加表名前缀 table-prefix: tb_ # 枚举类型所在包位置 type-enums-package: com.example.ssmpdemo.enums
  • 编写测试类
Copy
package com.example.ssmpdemo.service; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class BookServiceImplTest { @Autowired IBookService bookService; @Test void test(){ bookService.list(); } }

常用注解#

  • 自动映射字段名和表名
  • 自动映射表字段的下划线(不包括中划线)和java对象中的驼峰命名

设置表名(@TableName)#

映射数据库的表名,如果已经定义了表格前缀,则不受前缀的影响(即TableName中依然要加上前缀)。

Copy
@Data @TableName(value="user") public class Account { private Integer id; private String name; private Integer age; }

设置实体类字段 (@TableField)#

  • value 映射非主键字段名
  • exist 表示是否为数据库字段,取值为false则忽略该属性
  • select 是否查询该字段,取值为false则不查询该属性
  • fill 监听修改数据的动作。将对象存入数据库的时候,自动赋值,比如 create_time, update_time

通过 @TableField(fill=FieldFill.INSERT_UPDATE) 更新时间字段#

可选枚举类型:

Copy
public enum FieldFill { DEFAULT, INSERT, UPDATE, INSERT_UPDATE; private FieldFill() { } }

在需要更新的字段上添加 @TableField(fill = FieldFill.INSERT)

Copy
@Data @TableName(value="user") public class User { @TableId(value="id", type = IdType.NONE) private Long id; private String name; private Integer age; @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; }

编写时间更新策略。

Copy
/**3.0 版本之前需要在application.yml中配置,新版本用注解注入即可 global-config: meta-object-handler: top.twilight0319.mybatisplus.handler.TimeHandler **/ @Component public class TimeHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { setFieldValByName("createTime", new Date(), metaObject); setFieldValByName("updateTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { setFieldValByName("updateTime", new Date(), metaObject); } }

测试

Copy
@Test public void update(){ User user = new User(); user.setName("张三"); user.setId(1393736407226277889L); userMapper.updateById(user); }

设置主键字段和策略 (@TableId)#

  • value 映射主键字段的名字,把当前属性匹配到主键字段
  • type 设置主键类型,主键的生成策略, 取值来自枚举类 IdType
Copy
@Data @TableName(value="user") public class User { @TableId(value="id", type = IdType.ASSIGN_UUID) private String id; private String name; private Integer age; }
Copy
public enum IdType { AUTO(0), NONE(1), INPUT(2), ASSIGN_ID(3), ASSIGN_UUID(4), /** @deprecated */ @Deprecated ID_WORKER(3), /** @deprecated */ @Deprecated ID_WORKER_STR(3), /** @deprecated */ @Deprecated UUID(4); private final int key; private IdType(int key) { this.key = key; } public int getKey() { return this.key; } }
策略 含义
NONE 默认。如果用户没有指定值,则自动分配(雪花算法)
AUTO 完全采用数据库的策略,忽略用户的输入。数据库自增
INPUT 完全采用开发者的策略,需要开发者手动赋值,如果不赋值则传入NULL
ASSIGN_ID 自动分配(雪花算法)。Long、Integer、String
ASSIGN_UUID MP分配UUID。只能是String
  • 默认情况下(NONE),优先采用用户指定的值。雪花算法的值需要用Long保存。数据库中的字段也需要足够大(bigint)。
    Copy
    ==> Preparing: INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? ) ==> Parameters: 100(Long), 小明(String), 15(Integer) <== Updates: 1
  • INPUT情况下,如果不传入主键,则生成的SQLIDNULL。如果数据库设置了自增,那就使用自增策略,没有就报错 (Column 'id' cannot be null)(这是数据库的策略)。
    Copy
    ==> Preparing: INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? ) ==> Parameters: null, 小明(String), 15(Integer) <== Updates: 1
  • AUTO 情况下,生成的SQl根本没有ID。所以能不能成功完全取决于数据库。
    Copy
    ==> Preparing: INSERT INTO user ( name, age ) VALUES ( ?, ? ) ==> Parameters: 小明(String), 15(Integer)
  • ASSIGN_IDASSIGN_UUID 都会优先考虑用户的输入。只要User对象中的字段类型和数据库保持一致就不会报错。
    • ASSIGN_ID 生成的数值会自动转换成String类型的id。所以他也可以保存到字符串字段中。
    • ASSIGN_UUID 生成的值无法转换成数值,所以只能用String

乐观锁(@Version)#

通过version字段保证数据的安全性,当修改数据的时候会以version作为条件,当条件成立才会修改成功。每次修改数据会自动修改version字段的值,update ... set version = 2 where version = 1

  1. 数据库中需要有一个字段用来记录乐观锁的version
  2. 在实体类中使用注解标记当前字段用于乐观锁。
    Copy
    @Version private Integer version;
  3. 添加 mybatis-plus-extension 3.4.2 依赖。
  4. 通过设置拦截器的方式添加组件。
Copy
/** * 通过设置拦截器的方式添加组件。OptimisticLockerInterceptor已弃用 * @return Mybatis-plus的拦截器 MybatisPlusInterceptor */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor= new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁 return interceptor; }

只会修改一条,因为version字段被修改了

Copy
User user1 = userMapper.selectById(1393736407226277889L); user1.setName("李四"); User user2 = userMapper.selectById(1393736407226277889L); user2.setName("王五"); userMapper.updateById(user1); userMapper.updateById(user2);
Copy
==> Preparing: UPDATE user SET name=?, age=?, create_time=?, update_time=?, version=? WHERE id=? AND version=? ==> Parameters: 李四(String), 15(Integer), 2021-05-16 09:13:49.0(Timestamp), 2021-05-16 10:12:39.051(Timestamp), 2(Integer), 1393736407226277889(Long), 1(Integer) <== Updates: 1 ==> Preparing: UPDATE user SET name=?, age=?, create_time=?, update_time=?, version=? WHERE id=? AND version=? ==> Parameters: 王五(String), 15(Integer), 2021-05-16 09:13:49.0(Timestamp), 2021-05-16 10:12:39.058(Timestamp), 2(Integer), 1393736407226277889(Long), 1(Integer) <== Updates: 0

使用枚举类型 (@EnumValue)#

  1. 编写枚举类,注意将value字段(即与数据库中的数值对应的字段)加上 @EmunValue 注解
  2. application.yml 中添加 mybatis-plus.type-enums-package: com.example.ssmpdemo.enums 配置枚举类型所在的 package
  3. 在实体类中将字段声明为枚举类型: private MyEnum status;

编写枚举类,两者都需要在yml文件中配置包路径:

Copy
// 枚举类型 public enum MyEnum { WORK(0, "上班"), REST(1, "休息"); @EnumValue private Integer code; private String msg; MyEnum(Integer code, String msg){ this.code = code; this.msg = msg; } } // 或者使用mybatisplus提供的IEnum接口 public enum MyEnum2 implements IEnum<Integer> { WORK(0, "上班"), REST(1, "休息"); @EnumValue private Integer code; private String msg; MyEnum2(Integer code, String msg){ this.code = code; this.msg = msg; } @Override public Integer getValue() { return this.code; } }

配置枚举类型所在的package

Copy
mybatis-plus: type-enums-package: top.twilight0319.mybatisplus.enums

实体类:

Copy
@Data @TableName(value="user") public class User { @TableId(value="id", type = IdType.NONE) private Long id; private String name; private Integer age; @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; @TableField(value = "version") @Version private int versionNumber; // 自动把数据库中的值转化为枚举类型 private MyEnum status; }

逻辑删除(@TableLogic)#

用一个字段表示该记录是否被删除,删除记录时不会真的删除记录,而是修改删除标记位。

  • 1.数据表添加deleted字段
  • 2.实体类添加注解
    Copy
    @TableLogic @TableField(value="deleted") private int deleted;
  • 添加配置
    Copy
    mybatis-plus: global-config: db-config: logic-not-delete-value: 0 logic-delete-value: 1

删除操作变成了 update 操作,如果没有@TableLogic就会执行 delete 操作

Copy
==> Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0 ==> Parameters: 1(Integer) <== Updates: 1

查询语句也会忽略已删除的记录(通过添加where条件):

Copy
==> Preparing: SELECT id,name,age,create_time,update_time,version AS versionNumber,status,deleted FROM user WHERE deleted=0 ==> Parameters: <== Columns: id, name, age, create_time, update_time, versionNumber, status, deleted

增删改查#

查询#

Copy
// 不加任何条件全部查询 userMapper.selectList(null).forEach(System.out::println); // 用wrapper 包装查询条件 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name", "小明"); wrapper.gt("age", 15); userMapper.selectList(wrapper).forEach(System.out::println); // allEq 多条件, ne 不等于, lt 小于, gt 大于, ge, QueryWrapper<User> wrapper = new QueryWrapper<>(); Map<String, Object> map = new HashMap<>(); map.put("name", "小明"); map.put("age", 15); wrapper.allEq(map); userMapper.selectList(wrapper).forEach(System.out::println); // 模糊查询 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.like("name", "小"); // %小% wrapper.likeLeft(); // %小 wrapper.linkRight(); // 小% userMapper.selectList(wrapper); // 联合查询 inSQL QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.inSql("id", "select id from user where id > 200"); wrapper.inSql("id", "select id from user where id < 1393839033125179394"); userMapper.selectList(wrapper).forEach(System.out::println); ==> Preparing: SELECT id,name,age,create_time,update_time,version AS versionNumber,status,deleted FROM user WHERE deleted=0 AND (id IN (select id from user where id > 200) AND id IN (select id from user where id < 1393839033125179394)) ==> Parameters: // 排序 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); userMapper.selectList(wrapper).forEach(System.out::println); // 先筛选再排序 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); wrapper.gt("id", 200); userMapper.selectList(wrapper).forEach(System.out::println); ==> Preparing: SELECT id,name,age,create_time,update_time,version AS versionNumber,status,deleted FROM user WHERE deleted=0 AND (id > ?) ORDER BY id DESC ==> Parameters: 200(Integer)
Copy
// 根据指定id查询 User user = userMapper.selectById(100); System.out.println(user); // 查询批量的id,不存在的id会跳过 userMapper.selectBatchIds(Arrays.asList(100, 101, 102)); // 只能做等值判断,逻辑判断需要wrapper Map<String, Object> map = new HashMap<>(); map.put("name", "小明"); map.put("age", 15); userMapper.selectByMap(map); // 查询数量 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("age", 15); userMapper.selectCount(wrapper); // 筛选后的计数 userMapper.selectCount(null); // 所有数量 // 将查询的结果封装到Map中 mapper.selectMaps(wrapper).forEach(...); mapper.selectList(wrapper).forEach(...);

分页查询#

Copy
// 帮助自动完成分页 @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); } // 返回第一页的数据,每页数据两条 Page<User> page = new Page<>(1, 2); Page<User> userPage = userMapper.selectPage(page, null); System.out.println(userPage.getCurrent()); // 返回当前页位置,从0开始 System.out.println(userPage.getSize()); // 返回分页的大小 System.out.println(userPage.getTotal()); // 返回表记录的总数 userPage.getRecords().forEach(System.out::println); // 返回当前页的记录 // 将返回结果封装成Map Page<Map<String, Object>> page = new Page<>(1, 2); List<Map<String, Object>> records = userMapper.selectMapsPage(page, null).getRecords(); records.forEach(System.out::println); ==> Preparing: SELECT id,name,age,create_time,update_time,version AS versionNumber,status,deleted FROM user WHERE deleted=0 LIMIT ?,? ==> Parameters: 0(Long), 2(Long) <== Columns: id, name, age, create_time, update_time, versionNumber, status, deleted <== Row: 1, tom, 10, null, null, 0, 0, 0 <== Row: 100, 小明, 15, null, null, 0, 0, 0 <== Total: 2
Copy
// 返回所有主键 userMapper.selectObjs(null).forEach(System.out::println);
Copy
// 查询一个,结果只能是一条记录,否则会报错。 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("id", 100); userMapper.selectOne(wrapper);

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 10

自定义结果集#

有实体类如下:

Copy
@Data public class UserDto extends User { private Role role; private String roleName; private List<Deal> dealList; private List<Deal> dealList2; private List<String> dealNameList; }
  • @Results 可以实现XML文件中<ResultMap>一样的功能。可以定义字段的映射关系、多表联查。id 表示当前结果集的名称,可以用 @ResultMap 来引用他。
  • @Result 是针对某一个字段进行配置。
  • property 表示实体类的属性名称。
  • column 表示表中的字段名称
  • javaType 表示字段的类型。
  • id=true 是否是主键。
  • many=@Many() 一对多查询。
  • one=@one() 一对一或者多对一查询。
  • fetchType fetch策略,立即获取还是推迟获取。
Copy
@Results( id = "userDtoList", value = { @Result(property = "id", column = "id", javaType = Integer.class, id = true), @Result(property = "username", column = "username"), @Result(property = "age", column = "age", javaType = Integer.class), @Result(property = "weight", column = "weight", javaType = Float.class), @Result(property = "roleId", column = "role_id", javaType = Integer.class), @Result(property = "role", column = "role_id", javaType = Role.class, one = @One(select = "com.example.mybatisplusdemo.mapper.RoleMapper.getRoleById", fetchType = FetchType.EAGER)), @Result(property = "dealList", column = "id", javaType = List.class, many = @Many(select = "com.example.mybatisplusdemo.mapper.DealMapper.getDealListByUserId", fetchType = FetchType.LAZY)), @Result(property = "dealList2", column = "{id=id, name=username}", javaType = List.class, many = @Many(select = "com.example.mybatisplusdemo.mapper.DealMapper.getDealNameListByUserIdAndName", fetchType = FetchType.LAZY)), @Result(property = "dealNameList", column = "id", javaType = List.class, one = @One(select = "com.example.mybatisplusdemo.mapper.DealMapper.getDealNameListByUserId", fetchType = FetchType.EAGER)) } ) @Select("select * from users") List<UserDto> getUserList();
  • @Many@Oneselect 部分需要填写某个查询方法的引用, 此时将 @Result 中的 column 值传入到方法中进行关联查询。
  • column = "{方法参数名=当前表字段名, 方法参数名=当前表字段名}" 的属性可以填写多个,这里的方法参数名需要和SQL语句中的#{att}部分保持一直。
Copy
@Mapper public interface DealMapper extends BaseMapper<Deal> { @Select("select * from deal where user_id=#{id}") List<Deal> getDealListByUserId(@Param("id") Integer id); @Select("select content from deal where user_id=#{id}") List<String> getDealNameListByUserId(@Param("id") Integer id); /** * id 和 name 需要和注解中的字段名称保持一致。 * @param id * @param name * @return */ @Select("select * from deal where user_id=#{id} and username=#{name}") List<Deal> getDealNameListByUserIdAndName(@Param("id") Integer id, @Param("name") String name); }

添加#

只有这一个方法

Copy
User user = new User(); user.setId(11111L); user.setName("小红"); user.setAge(25); userMapper.insert(user); System.out.println(user);

删除#

Copy
@Test public void delete(){ // 根据id删除 int i1 = userMapper.deleteById(1); // 根据id列表删除 int i2 = userMapper.deleteBatchIds(Arrays.asList(1393605031051239426L, 11111L)); // 根据wrapper的条件删除 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("age", 15); int i3 = userMapper.delete(wrapper); // 根据map指定的等式删除 Map<String, Object> map = new HashMap<>(); map.put("name", "李四"); int i4 = userMapper.deleteByMap(map); }

修改#

  • 只有在传入user对象的时候,才会调用 fillHandler 和乐观锁 @Version。(即使user所有属性都为null也是一样)
  • 空的user对象,version默认是0, 所以会被update1

1. 根据id修改#

Copy
User user = new User(); user.setId(1L); mapper.updateById(user);

2. 根据 wrapper 修改#

  1. 根据wrapper中的条件, 把指定行的值修改为user中的属性值。

    Copy
    User user = userMapper.selectById(1); user.setName("小红"); QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("age", 25); userMapper.update(user, wrapper);
    Copy
    ==> Preparing: UPDATE user SET name=?, age=?, update_time=?, version=?, status=? WHERE deleted=0 AND (age = ? AND version = ?) ==> Parameters: 小红(String), 10(Integer), 2021-05-25 01:03:38.129(Timestamp), 1(Integer), 0(Integer), 25(Integer), 0(Integer) <== Updates: 1

    对于只有一个属性的实体,会忽略其他的属性值。(update_time和乐观锁version除外)

    Copy
    User user = new User(); user.setName("小红"); QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("age", 25); userMapper.update(user, wrapper);
    Copy
    ==> Preparing: UPDATE user SET name=?, update_time=?, version=? WHERE deleted=0 AND (age = ? AND version = ?) ==> Parameters: 小红(String), 2021-05-25 01:07:16.463(Timestamp), 1(Integer), 25(Integer), 0(Integer) <== Updates: 0
  2. 使用 UpdateWrapper 替换实体类的赋值。(会忽略update_time和乐观锁version

    Copy
    UpdateWrapper<User> wrapper = new UpdateWrapper<>(); wrapper.set("age", 30).eq("name", "小红"); userMapper.update(null, wrapper);
    Copy
    ==> Preparing: UPDATE user SET age=? WHERE deleted=0 AND (name = ?) ==> Parameters: 30(Integer), 小红(String) <== Updates: 1

    User类和Wrapper中出现了重复的条件时,各个条件都会被依次追加到set语句中。顺序是先追加User的非空属性,再追加wrapper中定义的属性,所以wrapper中的属性会覆盖User. User 中的属性会被wrapper覆盖。

    Copy
    UpdateWrapper<User> wrapper = new UpdateWrapper<>(); wrapper.set("age", 30).eq("name", "小红"); User user = new User(); user.setAge(50); userMapper.update(user, wrapper);
    Copy
    // 有两个age ==> Preparing: UPDATE user SET age=?, update_time=?, version=?, age=? WHERE deleted=0 AND (name = ? AND version = ?) ==> Parameters: 50(Integer), 2021-05-25 01:19:17.147(Timestamp), 1(Integer), 30(Integer), 小红(String), 0(Integer) <== Updates: 0

3. 使用 lambda构造器#

Copy
LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>(); lambdaUpdateWrapper.eq(User::getName, "小红").set(User::getAge, 30); User user = new User(); user.setAge(50); Integer rows = userMapper.update(user, lambdaUpdateWrapper);

和之前一样会覆盖User中的属性

Copy
==> Preparing: UPDATE user SET age=?, update_time=?, version=?, age=? WHERE deleted=0 AND (name = ? AND version = ?) ==> Parameters: 50(Integer), 2021-05-25 01:29:57.392(Timestamp), 1(Integer), 30(Integer), 小红(String), 0(Integer) <== Updates: 0

lambdaQuery() 和 lambdaUpdate()#

可以通过 lambdaQuery()lambdaUpdate() 实现方便的查询和更新操作。

Copy
// 查询 accountService.lambdaQuery() .eq(true, Account::getName, "tom") .list() .forEach(System.out::println); // 更新,wrapper 的优先级更高 Account account = new Account(); account.setName("tom"); account.setMoney(20000.0); boolean jerry = accountService.lambdaUpdate() .eq(true, Account::getName, "jerry") .set(true, Account::getMoney, 10000) .update(account);

自定义SQL#

注解#

  • 编写 DeviceVO. 属性名和字段名要保持一致,这样才能对应上。
    Copy
    @Data public class DeviceVO { private int id; private String name; private BigInteger userId; private String userName; }
  • 编写mapper方法
    Copy
    @Select(value="select device.*, user.name user_name from device, user where device.user_id=user.id and user.id=#{id};") List<DeviceVO> deviceList(Integer id);
  • 测试
    Copy
    userMapper.deviceList(100).forEach(System.out::println);

在 mapper.xml文件中定义自定义语句#

  • IDEA中不会将src下的配置文件复制到target目录下。所以最好放在resources目录下。默认目录是 resources/mapper/*.xml,可以手动配置所需要的xml文件
    Copy
    mybatis-plus: # 默认是/mapper/*.xml mapper-locations: /mapper/*.xml
  • #{user.name}${eq.customSqlSegment} 不同。#是占位符?,之后的可以将变量的值替换?,而$是字符串拼接。比如`select * from user WHERE (id = ?) `中的`WHERE (id = ?)`就是`select * from user $` 的结果
  • #{}可以直接填属性名,而${}需要加上@Param才可以。

select#

  • 如果返回单条结果,那么方法的返回值不能写List<User>。但是反过来是可以的,用List<User>也能接受一个对象。
    Copy
    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 10
  • 返回List时,xml中的resultType="top.twilight0319.mybatisplus.entity.DeviceVO"直接写到对应实体类就行。
Copy
// 返回多表查询的List List<DeviceVO> deviceList(Integer id); // 返回单表的List List<User> userList(); // 使用wrapper List<User> userListWrapper(@Param(Constants.WRAPPER) Wrapper<User> wrapper); // 使用Map保存结果 Map<String, Object> userListMap(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
Copy
<select id="deviceList" resultType="top.twilight0319.mybatisplus.entity.DeviceVO"> select device.*, user.name user_name from device, user where device.user_id=user.id and user.id=#{id}; </select> <select id="userList" resultType="top.twilight0319.mybatisplus.entity.User"> select * from user where deleted=0; </select> <select id="userListWrapper" resultType="top.twilight0319.mybatisplus.entity.User"> select * from user ${ew.customSqlSegment} </select> <select id="userListMap" resultType="java.util.Map"> select * from user ${ew.customSqlSegment} </select>

insert、update、delete#

<insert>中不用加返回类型,接口中可以返回 int, long, boolean。如果返回值大于0则boolean为true,否则为false

Copy
// 可以在mapper.xml中使用 #{user.attrname} // 如果不加 @Param 就直接写user的属性名 int insertUser(@Param("user") User user);
Copy
<insert id="insertUser" > INSERT INTO user (id, name, age) VALUES(#{user.id}, #{user.name}, #{user.age}); </insert>

代码生成器#

根据数据表自动生成实体类,Mapper, Service,ServiceImpl, Controller

  1. pom.xml 导入 Mybatisplus generator
Copy
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.3.1</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency>

使用时直接修改参数即可

Copy
package top.twilight0319.mybatisplus; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; public class Main { public static void main(String[] args) { //创建generator对象 AutoGenerator autoGenerator = new AutoGenerator(); //数据源 DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setDbType(DbType.MYSQL); dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/dbtest?useUnicode=true&characterEncoding=UTF-8"); dataSourceConfig.setUsername("root"); dataSourceConfig.setPassword("123456"); dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver"); autoGenerator.setDataSource(dataSourceConfig); //全局配置 GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java"); globalConfig.setOpen(false); // true时会打开一个windows资源管理器 globalConfig.setAuthor("twilight"); globalConfig.setServiceName("%sService"); // 避免接口前出现I autoGenerator.setGlobalConfig(globalConfig); //包信息 PackageConfig packageConfig = new PackageConfig(); packageConfig.setParent("top.twilight0319.mybatisplus.generate"); packageConfig.setController("controller"); packageConfig.setService("service"); packageConfig.setServiceImpl("service.impl"); packageConfig.setMapper("mapper"); packageConfig.setEntity("entity"); autoGenerator.setPackageInfo(packageConfig); //配置策略 StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setEntityLombokModel(true); strategyConfig.setInclude("user", "device"); // 只映射部分表 strategyConfig.setNaming(NamingStrategy.underline_to_camel); strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); autoGenerator.setStrategy(strategyConfig); autoGenerator.execute(); } }

注意下面两个包的版本要一致:

Copy
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.3.1</version> </dependency>
posted @   twilight0402  阅读(1156)  评论(0编辑  收藏  举报
编辑推荐:
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 用 C# 插值字符串处理器写一个 sscanf
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
点击右上角即可分享
微信分享提示
CONTENTS