title: Mybatis Plus逻辑删除时自动填充时间
date: 2022-04-02 13:20:13
tags:
- Mybatis Plus
- SQL
categories: - Java
cover: ‘https://images.pexels.com/photos/290470/pexels-photo-290470.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260’
1、背景
开发规范里面要求有 delte_time 字段,但是baomidou的Mybatis plus里面没有这个字段;理论上也是不需要这个字段的,毕竟逻辑删除 与 update_time 配合起来就是 delte_time 的含义了;所以我们需要来研究一下 如何增加这个字段,并实现自动填充。
2、开发环境
java -version
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/>
</parent>
3、通用字段
1、SQL脚本:
`id` bigint(32) unsigned NOT NULL AUTO_INCREMENT,
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
`delete_time` timestamp NULL DEFAULT NULL COMMENT '删除时间',
`is_deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除标识位',
`version` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '版本号',
2、对应的BaseModel.java
@TableId(value = "id", type = IdType.AUTO)
protected Long id;
@TableField(value = "create_time", fill = FieldFill.INSERT)
protected Date createTime;
@TableField(value = "update_time", fill = FieldFill.UPDATE)
protected Date updateTime;
@TableField(value = "delete_time")
protected Date deleteTime;
@TableLogic
@TableField(value = "is_deleted")
protected Integer isDeleted;
@Version
@TableField(value = "version", fill = FieldFill.UPDATE, update = "%s+1")
protected Integer version;
@TableId:主键字段
@Version:版本号字段
@TableLogic:逻辑删除字段
FieldFill.UPDATE:当更新时填充
FieldFill.INSERT:当插入时填充
根据SQL脚本和BaseModel的相关字段,我们可以发现并没有对各个时间字段进行特殊处理,这里是有一个专门的【自动填充控制器】来操作的;
4、自定义填充控制器
@Configuration
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
Date now = new Date();
// 注意此处字段名称需要使用驼峰
Object createdAt = this.getFieldValByName("createTime", metaObject);
if (null == createdAt) {
this.setFieldValByName("createTime", now, metaObject);
}
Object updatedAt = this.getFieldValByName("updateTime", metaObject);
if (null == updatedAt) {
this.setFieldValByName("updateTime", now, metaObject);
}
Object version = this.getFieldValByName("version", metaObject);
if (null == version) {
this.setFieldValByName("version", 0L, metaObject);
}
}
@Override
public void updateFill(MetaObject metaObject) {
Object updatedAt = this.getFieldValByName("updateTime", metaObject);
if (null == updatedAt) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
Object version = this.getFieldValByName("version", metaObject);
if (null == version) {
this.setFieldValByName("version", 0L, metaObject);
}
}
}
到了这一步,逻辑删除时并不能对新增的 delete_time 字段设置当前的时间,还不符合我们的需求,所以我们需要改写一下SQL注入的逻辑;即:逻辑删除的时候,将 delete_time 设置为当前时间戳。
5、自定义SQL注入器
1、自定义逻辑删除,单个删除
public class LogicDeleteById extends AbstractMethod {
/**
* mapper 对应的方法名
*/
private static final String MAPPER_METHOD = "logicDeleteById";
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql;
SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;
if (tableInfo.isLogicDelete()) {
List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream()
.filter(i -> i.getFieldFill() == FieldFill.UPDATE || i.getFieldFill() == FieldFill.INSERT_UPDATE)
.collect(toList());
if (CollectionUtils.isNotEmpty(fieldInfos)) {
String sqlSet = "SET " + fieldInfos.stream().map(i -> i.getSqlSet(EMPTY)).collect(joining(EMPTY))
+ tableInfo.getLogicDeleteSql(false, true)
+ ",delete_time = NOW()";
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(),
tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, false));
} else {
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlLogicSet(tableInfo),
tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
tableInfo.getLogicDeleteSql(true, false));
}
} else {
sqlMethod = SqlMethod.DELETE_BY_ID;
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),
tableInfo.getKeyProperty());
}
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addUpdateMappedStatement(mapperClass, modelClass, MAPPER_METHOD, sqlSource);
}
}
2、自定义批量逻辑删除
public class LogicDeleteBatch extends AbstractMethod {
/**
* mapper 对应的方法名
*/
private static final String MAPPER_METHOD = "logicDeleteBatch";
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql;
if (tableInfo.isLogicDelete()) {
String logicDeleteSql = "<script>\nUPDATE %s %s %s\n</script>";
List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream()
.filter(i -> i.getFieldFill() == FieldFill.UPDATE || i.getFieldFill() == FieldFill.INSERT_UPDATE)
.collect(toList());
if (CollectionUtils.isNotEmpty(fieldInfos)) {
String sqlSet = "SET " + fieldInfos.stream().map(i -> i.getSqlSet(ENTITY_DOT)).collect(joining(EMPTY))
+ tableInfo.getLogicDeleteSql(false, true)
+ ",delete_time = NOW()";
sql = String.format(logicDeleteSql, tableInfo.getTableName(), sqlSet,
sqlWhereEntityWrapper(true, tableInfo));
} else {
sql = String.format(logicDeleteSql, tableInfo.getTableName(), sqlLogicSet(tableInfo),
sqlWhereEntityWrapper(true, tableInfo));
}
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addUpdateMappedStatement(mapperClass, modelClass, MAPPER_METHOD, sqlSource);
} else {
String deleteSql = "<script>\nDELETE FROM %s %s\n</script>";
sql = String.format(deleteSql, tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addDeleteMappedStatement(mapperClass, MAPPER_METHOD, sqlSource);
}
}
}
3、自定义SQL注入器
public class MybatisPlusSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 拿到父类的getMethodList方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 根据id删除并自动填充 LogicDeleteById
methodList.add(new LogicDeleteById());
methodList.add(new LogicDeleteBatch());
return methodList;
}
}
4、在baseMapper里面声明这两个方法,使用过程中就可以直接在其他mapper中调用这两个自定义方法了:
public interface IBaseMapper<T> extends BaseMapper<T> {
/**
* 逻辑删除
*
* @param entity
* @return
*/
int logicDeleteById(T entity);
/**
* 批量逻辑删除
*
* @param entity
* @param wrapper
* @return
*/
int logicDeleteBatch(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> wrapper);
}
6、集成到service层
1、接口
public interface IBaseService<T> extends IService<T> {
/**
* 逻辑删除
*
* @param entity
* @return
*/
int logicDeleteById(T entity);
/**
* 批量逻辑删除
*
* @param entity
* @param wrapper
* @return
*/
int logicDeleteBatch(T entity, Wrapper<T> wrapper);
}
2、实现类,记得要继承ServiceImpl<M, T>,不然要写很多相同的代码
public class IBaseServiceImpl<M extends IBaseMapper<T>, T> extends ServiceImpl<M, T> implements IBaseService<T> {
@Autowired
protected M baseMapper;
@Override
public M getBaseMapper() {
return baseMapper;
}
@Override
public int logicDeleteById(T entity) {
return baseMapper.logicDeleteById(entity);
}
@Override
public int logicDeleteBatch(T entity, Wrapper<T> wrapper) {
return baseMapper.logicDeleteBatch(entity, wrapper);
}
}
3、至此,就可以在service层进行直接调用了,自从用了mybatis plus,就远离了mapper.xml