本是想跟着尚医通这个项目来做个完整的项目,结果视频的结构把我整麻了。进行一个跑路。但拜其所赐,对 MP 进行了一些学习。
MyBatis-Plus(下面简称 MP)是对 MyBatis 的增强和扩展,它不改变 MyBatis 的 API ,但是提供了更多强大的功能以帮助编码。
对 MP 最基础的使用非常简单——让 Mapper 接口继承一个所谓的BaseMapper
接口即可,这个BaseMapper
是泛型接口,其类型为操作的实体(当然,对应数据库的一张表)。
在官方示例中,使用MapperScan
注解对 Mapper 进行扫描和注入,但我认为使用Mapper
注解语义上更为清晰,且 IDE 支持友好(我这 IDEA 不能识别 MapperScan 所导入的 Bean,不知道为何)。
@Data public class User { @TableId private Long id; private String name; private Integer age; private String email; }public interface UserMapper extends BaseMapper <User> {}
简单 CRUD BaseMapper
中定义了许多 CRUD 方法,能够满足大多数业务需求。许多方法可以通过传入所谓的Wrapper
对象给定查询/更改条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 userMapper.selectById(2 ); userMapper.selectList(null ); userMapper.selectOne(new QueryWrapper <User>().eq("name" , "Tom" ));
selectBatchIds
方法接受一个主键 Id 列表并返回结果集合,但或许查询多条记录时最实用的方法是selectByMap
,其接受一个Map
,k 为字段名,v 为值,返回 List,这种形式和 where 后接一连串相等运算符的形式基本等效,更复杂的查询得去使用QueryWrapper
了。
userMapper.selectBatchIds(Arrays.asList(1 , 2 , 3 ,)); Map<String, Object> selecter = new HashMap <>(); selecter.put("age" , 28 ); userMapper.selectByMap(selecter);
关于添加操作,MP 提供了方法,使能够通过实体的实例进行添加。
User newUser = User.builder() .id(10L ) .age(20 ) .email("abcd@163.com" ) .name("Ri" ) .build(); userMapper.insert(newUser);
可以配置如下项让 MP 输出日志到标准输出流——
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
关于 MP 提供的UpdateById
方法,该方法接受一个实体——其应当设定 Id 以及将要修改的域(其它的设为空),这个方法将会根据该实体的 ID 和非 null 的属性自动生成对应 SQL 语句,这种方法非常方便,特别是结合建造者模式!比如下面的实例——
User user = User.builder() .id(1L ) .name("omgwtf" ) .age(1020 ) .build(); userMapper.updateById(user);
关于删除操作,MP 提供了delete
,deleteById
,deleteBatchIds
,deleteByMap
方法,其分别接受 Wrapper,主键 Id,Id 集合,哈希表,满足了大多数操作需要。
Wrapper Wrapper 是 MP 提供的用以生成相应 SQL 语句的对象,其对 SQL 的大部分(所有?)运算符进行了抽象和方法化,从而能够让用户能够不写一行 SQL 代码而对数据库进行操作。
就操作对象来说,Wrapper 分为 QueryWrapper 和 UpdateWrapper,顾名思义,其分别负责查询和更新/删除。
就操作形式来说,Wrapper 分为普通 Wrapper 和 LambdaWrapper,普通 Wrapper 在操作中一般接受 Map,LambdaWrapper 一般接受 Lambda 表达式——这显然是更加方便易读的。
Wrapper 通过链式调用进行使用。各个表达式默认通过 and 进行连接。但也可以显式指定为 or。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 new QueryWrapper <User>() .eq("name" , "Jone" ) .eq("age" , 18 ); new QueryWrapper <User>() .eq("name" , "Jone" ) .eq("age" , 18 ) .or() .eq("name" , "Jack" ) .eq("age" , 200 );new QueryWrapper <User>() .or(i->{ i.eq("name" , "Jone" ); i.eq("age" , 18 ); }) .or(i->{ i.eq("name" , "Jack" ); i.eq("age" , 200 ); });
自动填充 MP 还提供了自动填充的功能——在进行插入的时候,能自定义某些字段的填充。这种填充我在毕设项目时也进行过使用,当时是通过触发器进行实现的。
自动填充分为添加时设置值 以及修改时设置值 ,其通过INSERT
,DEFAULT
(默认不处理),INSERT_UPDATE
,UPDATE
这四个枚举指定。要使用自动填充需要进行两个操作——在字段上添加相关注解;定义自动填充处理器。下面展示了其定义和应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @Data @Builder public class User { @TableField(value = "create_time", fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; }@Component public class UserMapperHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { this .setFieldValByName("createTime" , new Date (), metaObject); this .setFieldValByName("updateTime" , new Date (), metaObject); } @Override public void updateFill (MetaObject metaObject) { this .setFieldValByName("updateTime" , new Date (), metaObject); } }
可以认为自动填充做了这样的工作——进行操作时,检查所有字段,查找所有带 TableField 注解且定义了 fill 属性的字段,并将符合条件的字段也 插入到 SQL 中(就像非 null 的字段),因此这些字段应当(不是必须)在处理器中进行赋值。
一个有趣的地方是,如果定义了多个处理器,Spring Boot 会提示注入失败——有多个可选 Bean,看来应当在配置文件中进行相关定义,而不是直接给处理器进行 Component 注解。
分页 分页在实践中可能是一个比较重要的话题,虽然我从来没写过,都是一口气全部拿到的 w。
MP 提供了分页查询的功能,其是以插件 的形式实现的,具体来说是自定义相应的 bean。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Configuration @MapperScan("me.ykn.mapper") public class MyBatisPlusConfig { @Bean public PaginationInnerInterceptor paginationInnerInterceptor () { return new PaginationInnerInterceptor (DbType.MYSQL); } @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor (); interceptor.addInnerInterceptor(paginationInnerInterceptor()); return interceptor; } }
分页查询的应用是简单的,许多方法提供了对应返回 Page 的方法。
Page<User> page = new Page <>(1 , 2 ); Page<User> res = userMapper.selectPage(page, null ); System.out.println(res.getRecords());
就这些吧。