mybatis-plus易忘点笔记
安装
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- jdk8, 分页插件分离到这个依赖了 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
</dependency><dependencyManagement><dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-bom</artifactId><version>3.5.15</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
新版本分页插件挪到mybatis-plus-jsqlparser依赖中了,并且这个依赖分成两个名称,因为jsqlparser 5.0不再支持jdk8了。
可参考官方文档-安装章节
分页
首先需要注册分页插件
@Configuration(proxyBeanMethods = false)
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();paginationInnerInterceptor.setDbType(DbType.MYSQL);interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}@Beanpublic MetaObjectHandler metaObjectHandler() {return new FillMetaObjectHandler();}
}
分页方法定义
方法的第一个参数必须是IPage对象
自定义方法
IPage<User> list(IPage<User> page, User condition);
BaseMapper内置方法
default <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {page.setRecords(selectList(page, queryWrapper));return page;
}
无论是内置方法还是自定义方法
如果返回类型是 IPage,则入参的 IPage 不能为 null,因为实际返回的IPage对象就是传入的参数IPage对象,会自动调用setRecords方法设置结果
如果返回类型是 List,则需要手动设置入参的 IPage.setRecords(返回的 List)。
临时不分页方案
-
若返回类型是List,则入参IPage对象传null即可。分页插件会判断如果为空则不拦截。
-
通用方案,初始化IPage对象时size参数传负数即可。
IPage<User> page = Page.of(1, -1);
只查数据不查count(但是需要分页)
将searchCount属性设置为false
IPage<User> page = Page.of(1, 10, false);
指定count方法
若不想要分页插件自动生成count方法,则指定Page对象的countId属性即可,countId可以不带名称空间,分页插件自己会拼接当前查询方法的名称空间。
适合复杂sql优化场景
自动填充字段
public class FillMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {/** 字段名称、类型需要和实体中的字段名称、类型相匹配* 实体中对应字段存在@TableField(fill = FieldFill.INSERT)或@TableField(fill = FieldFill.INSERT_UPDATE)* 若实体中对应字段本身有值, 则不会去覆盖它* 使用lambda表达式可以避免填充值提前创建*/this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);}@Overridepublic void updateFill(MetaObject metaObject) {/** 字段名称、类型需要和实体中的字段名称、类型相匹配* 实体中对应字段存在@TableField(fill = FieldFill.UPDATE)或@TableField(fill = FieldFill.INSERT_UPDATE)* 若实体中对应字段本身有值, 则不会去覆盖它* 使用lambda表达式可以避免填充值提前创建*/this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);}
}
实体
/*** 创建时间* updateStrategy = FieldStrategy.NEVER: 更新方法会忽略createTime*/
@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER)
private LocalDateTime createTime;/*** 修改时间*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
FillMetaObjectHandler需要注入到Spring容器中,可以通过 @Component 或 @Bean 注解来实现。
注意事项
- 如果实体中属性本身有值不会被覆盖,如果填充值为
null也不会去调用set方法进行填充赋值。 - 字段必须声明
@TableField注解,并设置fill属性来选择填充策略。 - 在
update(T entity, Wrapper<T> updateWrapper)时,entity不能为空,否则自动填充失效。
- 在
update(Wrapper<T> updateWrapper)时不会自动填充,需要手动赋值字段条件。
-
delete操作在符合逻辑删除时(变成了update)也会自动填充。 -
对于自定义方法,多参数时参数名必须是et
```java
// 单参数
@Update("update user set age = #{age}, update_time = #{updateTime} where id = #{id}")
int updateAgeById(User user);
/*
- 单参数带@Param注解或者多参数,参数名称为et的才会被填充
- 因为带@Param,底层真实的参数对象变成了map,需要一个标记来确定到底要填充map中的哪一个参数
*/
@Update("update user set age = #{et.age}, update_time = #{et.updateTime} where id = #{et.id}")
int updateAgeByIdWithParamName(@Param(Constants.ENTITY) User user);
```
逻辑删除
全局配置
mybatis-plus:global-config:db-config:logicDeleteField: delFlglogic-delete-value: 1logic-not-delete-value: 0
全局配置时必须实体类中有这个字段才会生效,没有则无效。
实体局部配置
@TableLogic(value = "0", delval = "1")
private Integer delFlg;
简要工作原理
- 插入:逻辑删除字段的值不受限制。
- 查找:自动添加条件,过滤掉标记为已删除的记录。
- 更新:防止更新已删除的记录。
- 删除:将删除操作转换为更新操作,标记记录为已删除。
例如:
- 删除:
update user set deleted=1 where id = 1 and del_flg = 0 - 查找:
select id,name,deleted from user where del_flg = 0 - 修改:
update user set name = 'xx' where del_flg = 0
对于插入
- 方法一:在数据库中为逻辑删除字段设置默认值。
- 方法二:在插入数据前手动设置逻辑删除字段的值。
- 方法三:使用 MyBatis-Plus 的自动填充功能。
注意:逻辑删除对自定义Mapper方法无效。
insert或者update方法默认拼接字段规则
当实体中字段值不为null时,就会包含在sql中。
可通过@TableField注解的insertStrategy以及updateStrategy指定。默认值为DEFAULT,因为全局配置的默认值也是DEFAULT,所以效果等同于NOT_NULL
public enum FieldStrategy {/*** 任何时候都加入 SQL*/ALWAYS,/*** 非NULL判断*/NOT_NULL,/*** 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)*/NOT_EMPTY,/*** 默认的,一般只用于注解里* <p>1. 在全局里代表 NOT_NULL</p>* <p>2. 在注解里代表 跟随全局</p>*/DEFAULT,/*** 不加入 SQL*/NEVER
}
因此如果想要修改字段值为null,最好使用wrapper
/** UPDATE user SET age=null WHERE del_flg=0 AND (id = 28)*/
ChainWrappers.lambdaUpdateChain(userMapper).set(User::getAge, null).eq(User::getId, 28).update();
批量新增修改方法
default List<BatchResult> insert(Collection<T> entityList) {return insert(entityList, Constants.DEFAULT_BATCH_SIZE);
}default List<BatchResult> updateById(Collection<T> entityList) {return updateById(entityList, Constants.DEFAULT_BATCH_SIZE);
}
可以和别的方法在一个事务中共存,批量方法是新开了一个ExecutorType=BATCH的DefaultSqlSession,注意不能是SqlSessionTemplate,如果是SqlSessionTemplate,底层执行时会去拿绑定在ThreadLocal中的DefaultSqlSession,其中会校验ExecutorType,不能进行修改。而新开一个DefaultSqlSession,由于Mybatis中的Transaction对象为SpringManagedTransaction,因此底层的Connection是共享的,由Spring事务管理器绑定在ThreadLocal中,因此能共享同一个事物,且这个新开的DefaultSqlSession调用commit时是没有任何效果的,由Spring事务管理器统一提交和回滚事务。
