说明

本文实现以下需求效果

  1. 创建数据时自动填充 createUserIdcreateTime
  2. 更新数据时自动填充 updateUserIdupdateTime(每次修改都自动填充新的 updateTime 值)
  3. 软删除数据时自动填充 deleteUserIddeleteTime

创建

实体类

为实体类(DO)的 createUserIdcreateTime 字段配置以下 Annotation,代表标记在插入(insert into)时自动填充字段值

@TableField(fill = FieldFill.INSERT)
private Integer createUserId; @TableField(fill = FieldFill.INSERT)
private Date createTime;

实现 MetaObjectHandler

如果实体(DO)中配置了上面的注解,就会执行这个 handler。这里在 insert 时自动填充 createUserIdcreateTime 字段的值

public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
private static final String createUserId = "createUserId";
private static final String createTime = "createTime"; @Override
public void insertFill(MetaObject metaObject) {
// insert 时自动插入 createUserId、createTime
this.strictInsertFill(metaObject, createUserId, StpUtil::getLoginIdAsInt, Integer.class);
this.strictInsertFill(metaObject, createTime, Date::new, Date.class);
// 关于 is_delete 字段,这里不提供默认值,因为官方推荐设置数据库中列的默认值
} @Override
public void updateFill(MetaObject metaObject) {}
}

配置

注入 IOC 容器。

MybatisPlusMetaObjectHandler 类上配置 @Configuration 注解也可以达到同样目的。

@Configuration
public class MybatisPlusConfig { @Bean
public MybatisPlusMetaObjectHandler mybatisPlusCommonDateFieldValueFillHandler(){
return new MybatisPlusMetaObjectHandler();
}
}

更新

实体类

在实体类(DO)上配置注解

@TableField(fill = FieldFill.UPDATE)
private Integer updateUserId; @TableField(fill = FieldFill.UPDATE)
private Date updateTime;

实现 MetaObjectHandler

如果实体(DO)中配置了上面的注解,就会执行这个 handler。这里在 update 时自动填充 updateUserIdupdateTime 字段的值

特别注意,这里需要重写 strictFillStrategy 方法,因为默认的行为不会按照预期执行(每次更新数据时都更新 updateTime)

public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
private static final String updateUserId = "updateUserId";
private static final String updateTime = "updateTime"; @Override
public void insertFill(MetaObject metaObject) {} @Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, updateUserId, StpUtil::getLoginIdAsInt, Integer.class);
this.strictUpdateFill(metaObject, updateTime, Date::new, Date.class);
} /**
* 严格模式填充策略,默认有值不覆盖,如果提供的值为null也不填充
*
* @param metaObject metaObject meta object parameter
* @param fieldName java bean property name
* @param fieldVal java bean property value of Supplier
* @return this
* @since 3.3.0
*/
@Override
public MetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier<?> fieldVal) {
// 但当自动填充字段为 updateTime 时,始终更新它
if (fieldName.equals(updateTime)) {
Object obj = fieldVal.get();
metaObject.setValue(fieldName, obj);
} else if (metaObject.getValue(fieldName) == null) { // 这个 if 是源码中的
Object obj = fieldVal.get();
if (Objects.nonNull(obj)) {
metaObject.setValue(fieldName, obj);
}
}
return this;
}
}

配置

参考创建章节中的配置,如果已经配置了就不需要再配置了

软删除

配置 application.yml

mybatis-plus:
global-config:
db-config:
logic-delete-field: is_delete # 逻辑删除属性名称(为数据库中的字段名称)
logic-delete-value: 1 # 代表已删除的值
logic-not-delete-value: 0 # 代表未删除的值

实体类

在实体类(DO)中对以下字段配置注解。

标识 isDelete 字段作为逻辑删除的标识,然后有两个需要自动填充值的字段。

/**
* 逻辑删除标识
*
* \@TableLogic\ 注解标记逻辑删除属性
*/
@TableLogic
private Boolean isDelete; @TableField(fill = FieldFill.UPDATE)
private Integer deleteUserId; @TableField(fill = FieldFill.UPDATE)
private Date deleteTime;

重写 DefaultSqlInjector

如果需要在软删除时自动填充其他字段的值,才需要重写这个 DefaultSqlInjector,否则忽略本小节。

这里配置了逻辑删除时填充其他字段值的 SqlInjector

public class MybatisPlusSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
final List<AbstractMethod> methods = super.getMethodList(mapperClass); methods.add(new LogicDeleteByIdWithFill()); return methods;
}
}

实现 MetaObjectHandler

由于 mybatis plus 的 @TableField 注解枚举字段 fill 的值没有我们想要的 Delete,所以 UpdateDelete 实际上都是走的 updateFill 函数,在函数内部我们需要区分出到底是更新还是软删除,才能继续下一步即自动填充不同行为下字段的值

注意,我们配置了 @TableLogic 注解后,isDelete 字段在软删除时 mybatis plus 会自动填充值,但是我们还是需要在软删除之前手动赋值一次,因为 updateFill 函数内我们需要区分行为,软删除之前如果不手动设置,就无法识别是软删除行为,就无法自动填充值了。

public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
private static final String isDelete = "isDelete";
private static final String updateUserId = "updateUserId";
private static final String deleteUserId = "deleteUserId";
private static final String updateTime = "updateTime";
private static final String deleteTime = "deleteTime"; @Override
public void insertFill(MetaObject metaObject) {} @Override
public void updateFill(MetaObject metaObject) {
// 软删除时,需要外面先 setIsDelete(true),否则无法识别是更新还是软删除操作
if (metaObject.hasGetter(isDelete) && metaObject.getValue(isDelete) == Boolean.TRUE) {
this.strictUpdateFill(metaObject, deleteUserId, StpUtil::getLoginIdAsInt, Integer.class);
this.strictUpdateFill(metaObject, deleteTime, Date::new, Date.class);
} else {
this.strictUpdateFill(metaObject, updateUserId, StpUtil::getLoginIdAsInt, Integer.class);
this.strictUpdateFill(metaObject, updateTime, Date::new, Date.class);
}
} /**
* 严格模式填充策略,默认有值不覆盖,如果提供的值为null也不填充
*
* @param metaObject metaObject meta object parameter
* @param fieldName java bean property name
* @param fieldVal java bean property value of Supplier
* @return this
* @since 3.3.0
*/
@Override
public MetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier<?> fieldVal) {
// 但当自动填充字段为 updateTime 时,始终更新它
if (fieldName.equals(updateTime)) {
Object obj = fieldVal.get();
metaObject.setValue(fieldName, obj);
} else if (metaObject.getValue(fieldName) == null) { // 这个 if 是源码中的
Object obj = fieldVal.get();
if (Objects.nonNull(obj)) {
metaObject.setValue(fieldName, obj);
}
}
return this;
}
}

配置(注入到 IOC)

我们需要将上面写的 MybatisPlusSqlInjector 注入 IOC 容器。

同样的,这两个 Bean 的配置,与在 MybatisPlusSqlInjectorMybatisPlusMetaObjectHandler 上配置 @Configuration 注解的效果一样。

@Configuration
public class MybatisPlusConfig { @Bean
public MybatisPlusSqlInjector mybatisPlusSqlInjector(){
return new MybatisPlusSqlInjector();
} @Bean
public MybatisPlusMetaObjectHandler mybatisPlusCommonDateFieldValueFillHandler(){
return new MybatisPlusMetaObjectHandler();
}
}

创建 MyBaseRepository

我这里将 Mapper 称为 Repository。因为我用到了 MapStruct 库,个人感觉它更适合被称为 Mapper

我们需要自己定义一个 MyBaseMapper 继承自 BaseMapper(mybatis plus 的),然后添加一个如下的方法签名,必须跟下面示例代码一样,因为我猜测内部是通过反射来调用的这个函数,所以方法签名必须一致,否则无法被 mybatis plus 库调用哦。

public interface MyBaseMapper<Entity>  extends BaseMapper<Entity> {
/**
* 逻辑删除填充其他字段的值
*
* @param entity 要删除的实体对象
* @return 受影响记录数量
*/
int deleteByIdWithFill(Entity entity);
}

我们原来的 Repository 继承自 mybatis plus 的 BaseMapper,现在我们需要修改为继承自上面新写的 MyBaseMapper

创建 MyBaseService

因为我希望 XXXService 中也有 deleteByIdWithFill 函数,所以这里我还自定义了 Service 的基类,如果不需要的话可以忽略本小节。

自定义的 MyBaseService 继承自 mybatis plus 的 IService<> 接口,然后添加如下方法签名,注意方法签名中的返回值是 boolean 而不是 int,因为写 Service 需要方便外部使用。

public interface MyBaseService<Entity> extends IService<Entity> {
/**
* 逻辑删除填充其他字段的值
*
* @param entity 要删除的实体对象
* @return 受影响记录数量
*/
boolean deleteByIdWithFill(Entity entity);
}

然后我们写一个 MyBaseService 的实现类。

注意这里我们需要继承自 mybatis plus 的 ServiceImpl 实现,减少我们自己实现的代码量,同时还需要实现我们的 MyBaseService 接口,然后内部写的巴巴适适的

public class MyBaseServiceImpl<EntityRepository extends MyBaseMapper<Entity>, Entity>
extends ServiceImpl<EntityRepository, Entity>
implements MyBaseService<Entity> {
/**
* 逻辑删除填充其他字段的值
*
* @param entity 要删除的实体对象
* @return 受影响记录数量
*/
@Override
public boolean deleteByIdWithFill(Entity entity) {
return SqlHelper.retBool(getBaseMapper().deleteByIdWithFill(entity));
}
}

最后,原来的 XXXService 修改为继承自新写的 MyBaseServiceXXXServiceImpl 修改为继承自新写的 MyBaseServiceImpl

public interface TestService extends MyBaseService<Test> {}

@Service
public class TestServiceImpl extends MyBaseServiceImpl<TestRepository, Test>
implements TestService{}

食用

Controller 中的使用方法

@PostMapping("delete")
public ResponseEntity<?> delete(@Valid @NotNull(message = CommonRule.error.whenParamNull) @RequestBody TestTestDeleteBodyVo body) {
final Test test = testService.getById(body.getId());
if (test == null) {
return ResponseEntity.ok().build();
} test.setIsDelete(true);
testService.deleteByIdWithFill(test); // 软删除必须是这样删除 return ResponseEntity.ok().build();
}

mybatis plus 增删改自动填充字段值的更多相关文章

  1. mybatis的增删改查返回值小析(六)

    本文验证了通过mybatis访问数据库时的,增删改查的返回值情况. 直接看代码. 1.service层 /** *@Author: Administrator on 2020/3/12 15:15 * ...

  2. MyBatis入门2_增删改查+数据库字段和实体字段不一致情况

    本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! 当数据库字段和实体bean中属性不一致时 之前数据库P ...

  3. 学习MyBatis必知必会(5)~了解myBatis的作用域和生命周期并抽取工具类MyBatisUtil、mybatis执行增删改查操作

    一.了解myBatis的作用域和生命周期[错误的使用会导致非常严重的并发问题] (1)SqlSessionFactoryBuilder [ 作用:仅仅是用来创建SqlSessionFactory,作用 ...

  4. mybatis之增删改

    前面三小节内容主要是针对查询操作进行讲解,现在对mybatis增删改进行演示. 由于每次建立工程比较复杂,可以参考第一节:mybatis入门来搭建一个简单的工程,然后来测试本节内容. 1.增 1.新增 ...

  5. 从0开始完成SpringBoot+Mybatis实现增删改查

    1.准备知识: 1)需要掌握的知识: Java基础,JavaWeb开发基础,Spring基础(没有Spring的基础也可以,接触过Spring最好),ajax,Jquery,Mybatis. 2)项目 ...

  6. Mybatis实例增删改查(二)

    创建实体类: package com.test.mybatis.bean; public class Employee { private Integer id; private String las ...

  7. [译]聊聊C#中的泛型的使用(新手勿入) Seaching TreeVIew WPF 可编辑树Ztree的使用(包括对后台数据库的增删改查) 字段和属性的区别 C# 遍历Dictionary并修改其中的Value 学习笔记——异步 程序员常说的「哈希表」是个什么鬼?

    [译]聊聊C#中的泛型的使用(新手勿入)   写在前面 今天忙里偷闲在浏览外文的时候看到一篇讲C#中泛型的使用的文章,因此加上本人的理解以及四级没过的英语水平斗胆给大伙进行了翻译,当然在翻译的过程中发 ...

  8. MyBatis的增删改查。

    数据库的经典操作:增删改查. 在这一章我们主要说明一下简单的查询和增删改,并且对程序接口做了一些调整,以及对一些问题进行了解答. 1.调整后的结构图: 2.连接数据库文件配置分离: 一般的程序都会把连 ...

  9. MyBatis批量增删改查操作

      前文我们介绍了MyBatis基本的增删该查操作,本文介绍批量的增删改查操作.前文地址:http://blog.csdn.net/mahoking/article/details/43673741 ...

随机推荐

  1. elasticsearch 5.6.7在线安装ik分词,亲测有效

    官网的在线安装命令 ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/rele ...

  2. npm run start 后台运行

    yum provides */nohup nohup npm start & 原程序的的标准输出被自动改向到当前目录下的nohup.out文件,起到了log的作用. 停止程序   ps -ef ...

  3. 遇到过的问题之“解决 No qualifying bean of type 问题”

    1.问题 解决 No qualifying bean of type 问题 2.思路: 1 检查是否添加了对应注解 2 检查配置是否正确,扫描包名, 类名及id是否正确 一 . 传统SSM项目 ssm ...

  4. 与和或(&&和||)比较的区别

    &&(短路与)和&(逻辑与)的时候: 有假则为假,全真则为真(有假必假,全真为真) ||(短路或)和|(逻辑或)的时候: 有真则为真,全假则为假(有真必真,全假为假)

  5. 智能指针中C++重载'->'符号是怎么实现的

    例如下面的代码: class StrPtr{ public: StrPtr() : _ptr(nullptr){} //拷贝构造函数等省略... std::string* operator->( ...

  6. Chrome 53 Beta一些有意思的改动

    原文链接: http://blog.chromium.org/2016...译者:Icarus邮箱:xdlrt0111@163.com 如果没有特殊说明的话,以下都是应用在Android,Chrome ...

  7. C#ASP.NET网站开发步骤

    1. 创建项目ASP.NET Web 应用程序. 2. 选择"Web 窗体"模板,然后单击 "确定" 按钮创建项目. 3. 在解决方案资源管理器中,右键添加we ...

  8. 【Android开发】【第三方SDK】 安卓版分词功能

    功能介绍: 获取剪切板内容,进行分词: 点击分解后的词,填入输入框: 点击叉号将地址拼接起来返回主界面 用途: 增加用户的体验效果,可以直接在微信上复制地址,然后通过此功能确认地址. 附上git地址 ...

  9. 使用 ssm 实现登录日志记录

    使用 ssm 实现登录日志记录 学习总结 一.基础准备 1. 实现效果 2. 数据表 2.1 登陆日志信息表 2.3 员工表 二.代码实现 1. SysLogLogin 实体类 2. LogAspec ...

  10. Ubuntu16.04 安装和卸载MySQL数据库

    Ubuntu16.04 安装和卸载MySQL数据库 1 安装 安装非常简单,只需要三个命令 1.1 安装服务端 sudo apt-get install mysql-server 在这一步过程中会有提 ...