文章用到的技术框架版本如下:
Mybatis Plus 3.3.2
Spring Boot 2.3.2.RELEASE
MySQL 8.0
设计背景
在设计表结构的时候,往往会有一些公用的字段,如创建人编号(create_user_id),最后修改人编号(last_update_user_id)、创建时间(create_time)、最后修改时间(last_update_time)等。
表结构
create table sys_user
(
user_id varchar(36) not null comment '用户编号',
user_name varchar(36) not null comment '用户名称',
password varchar(30) not null comment '密码(demo项目,明文录入)',
status int default 1 not null comment '状态(0:禁用,1:启用)',
create_time datetime null comment '创建时间',
update_time datetime null comment '修改时间',
create_user_id varchar(36) null comment '创建人编号',
update_user_id varchar(36) null comment '修改人编号',
primary key(user_id)
) comment '用户数据表';
在以往的项目开发的过程中,对上述字段的录入方式差不多都是这样的set方法:
//录入创建时间
sysUser.setCreateTime(new Date());
//录入创建人
sysUser.setCreateUserId(ShiroUtils.getUserId());
//other code .....
开发进度逐渐推进后,某天查看数据时出现了下方的情况:
等等!!修改人和修改时间怎么没了???看看代码:
/*
前台数据:
{
userId:'a15b6c54-e137-11ea-97ee-0242ac110002',
password:'xxx'
}
*/
/**
* 修改用户数据
* @param sysUser 用户数据
*/
public void updateUserData(SysUser sysUser){
this.baseMapper.updateById(sysUser);
}
直接入库了,没有对updateTime、updateUser字段做更新...
解决方案
方案描述
针对上述这样的情况,我们可以通过自定义Mybatis-plus提供的元对象字段填充控制器抽象类,实现公共字段自动写入。
废话不多说,直接上类:
元对象字段填充控制器抽象类
package com.qianlingo.mybatisplushandlerdemo.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Configuration;
/**
* MetaObjectHandler
* <p>
* 官方注释:
* 元对象字段填充控制器抽象类,实现公共字段自动写入
* 所有入参的 MetaObject 必定是 entity 或其子类的 MetaObject
* </p>
* @author qianlingo
* @date 2020/8/18
*/
@Configuration
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入元对象字段填充(用于插入时对公共字段的填充)
* 需字段加入
* 注解: TableField(fill = FieldFill.INSERT)
* or
* 注解: TableField(fill = FieldFill.INSERT_UPDATE)
* @param metaObject 元对象
*/
@Override
public void insertFill(MetaObject metaObject) {
}
/**
* 更新元对象字段填充(用于更新时对公共字段的填充)
* 需字段加入
* 注解: TableField(fill = FieldFill.UPDATE)
* or
* 注解: TableField(fill = FieldFill.INSERT_UPDATE)
* @param metaObject 元对象
*/
@Override
public void updateFill(MetaObject metaObject) {
}
}
我使用的是较新的3.3.2版本Mybatis-Plus,MetaObjectHandler类是通过实现进行自定义的,翻看了很多博文都是通过继承的,应该版本不同自定义的方式不同,大家可留意一下。
方法讲解
insertFill方法
在实体类字段顶上加入fii属性为INSERT或INSERT_UPDATE的@TableField注解后,进行数据库操作时会进入该方法。
/**
* 创建者
*/
@ApiModelProperty(value = "创建者")
@TableField(fill = FieldFill.INSERT)
private String createUserId;
updateFill方法
在实体类字段顶上加入fii属性为UPDATE或INSERT_UPDATE的@TableField注解后,进行数据库操作时会进入该方法。
对@TableField(fill=XXX)注解的全局配置,后面会花时间分Mybatis-plus不同版本进行讲解,本文不做过多展开。
/**
* 更新者
*/
@ApiModelProperty(value = "更新者")
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateUserId;
OK,接下来我们对两个方法具体的业务代码进行编写!
首先是insertFill方法:
/**
* 插入元对象字段填充(用于插入时对公共字段的填充)
* 需字段加入
* 注解: TableField(fill = FieldFill.INSERT)
* or
* 注解: TableField(fill = FieldFill.INSERT_UPDATE)
* @param metaObject 元对象
*/
@Override
public void insertFill(MetaObject metaObject) {
//填充create_time字段公用数据
if (metaObject.hasGetter("createTime") && metaObject.hasGetter("createTime")) {
setFieldValByName("createTime", new Date(), metaObject);
}
//填充 createUserId 字段公用数据
if (metaObject.hasGetter("createUserId") && metaObject.hasGetter("createUserId")) {
setFieldValByName("createUserId", ADMIN_USER_ID, metaObject);
}
//填充 updateTime 字段公用数据
if (metaObject.hasGetter("updateTime") && metaObject.hasGetter("updateTime")) {
setFieldValByName("updateTime", new Date(), metaObject);
}
//填充 updateUserId 字段公用数据
if (metaObject.hasGetter("updateUserId") && metaObject.hasGetter("updateUserId")) {
setFieldValByName("updateUserId", ADMIN_USER_ID, metaObject);
}
}
metaObject.hasGetter(String name) 方法: 返回boolean类型数据,判断传入的实体类是否有该属性(实体类中的属性名)的get方法。
setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) 方法:对某个属性(实体类中的属性名)赋值。
核心方法就是上述两行啦,insertFill方法的作用,是在做新增操作时将新增、修改人和时间都做填充。
理解上述后,updateFill方法就显得很简单了,即只在做修改操作时将修改人和时间做填充即可。
/**
* 更新元对象字段填充(用于更新时对公共字段的填充)
* 需字段加入
* 注解: TableField(fill = FieldFill.UPDATE)
* or
* 注解: TableField(fill = FieldFill.INSERT_UPDATE)
* @param metaObject 元对象
*/
@Override
public void updateFill(MetaObject metaObject) {
//填充 updateTime 字段公用数据
if (metaObject.hasGetter("updateTime") && metaObject.hasGetter("updateTime")) {
setFieldValByName("updateTime", new Date(), metaObject);
}
//填充 updateUserId 字段公用数据
if (metaObject.hasGetter("updateUserId") && metaObject.hasGetter("updateUserId")) {
setFieldValByName("updateUserId", ADMIN_USER_ID, metaObject);
}
}
编写测试类
package com.qianlingo.mybatisplushandlerdemo;
import java.util.UUID;
import com.qianlingo.mybatisplushandlerdemo.mybatis.domain.sys.SysUser;
import com.qianlingo.mybatisplushandlerdemo.service.sys.ISysUserService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
/**
* 用户测试类
*
* @author qianlingo
* @date 2020/8/18
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MybatisPlusHandlerDemoApplication.class)
public class SysUserTest {
@Resource
private ISysUserService iSysUserService;
@Test
public void addTest(){
//组装数据
SysUser sysUser = new SysUser();
String userId = UUID.randomUUID().toString();
System.out.println("userId:"+userId);
sysUser.setUserId(userId);
sysUser.setUserName("user"+UUID.randomUUID().toString().substring(0,10));
sysUser.setPassword("qianlingo");
sysUser.setStatus(1);
this.iSysUserService.save(sysUser);
getUserData();
}
@Test
public void updateTest(){
//组装数据
SysUser sysUser = this.iSysUserService.getById("");
sysUser.setPassword("qianlingooo");
this.iSysUserService.updateById(sysUser);
getUserData();
}
private void getUserData(){
SysUser sysUser = this.iSysUserService.getById("");
System.out.println(sysUser);
}
}
分别执行addTest、updateTest方法,可查看到数据变化:
2020-08-18 18:44:58.918 INFO 3819 --- [ main] c.q.mybatisplushandlerdemo.SysUserTest : 开始执行 addTest 方法
userId:c32ad96f-ff9f-4642-968c-89ef0a6fa4e0
2020-08-18 18:44:58.994 INFO 3819 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-08-18 18:44:59.280 INFO 3819 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-08-18 18:44:59.291 DEBUG 3819 --- [ main] c.q.m.m.mapper.sys.SysUserMapper.insert : ==> Preparing: INSERT INTO sys_user ( user_id, user_name, password, status, create_time, update_time, create_user_id, update_user_id ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )
2020-08-18 18:44:59.360 DEBUG 3819 --- [ main] c.q.m.m.mapper.sys.SysUserMapper.insert : ==> Parameters: c32ad96f-ff9f-4642-968c-89ef0a6fa4e0(String), user4d0f44a5-8(String), qianlingo(String), 1(Integer), 2020-08-18 18:44:58.989(Timestamp), 2020-08-18 18:44:58.989(Timestamp), a15b6c54-e137-11ea-97ee-0242ac110002(String), a15b6c54-e137-11ea-97ee-0242ac110002(String)
2020-08-18 18:44:59.374 DEBUG 3819 --- [ main] c.q.m.m.mapper.sys.SysUserMapper.insert : <== Updates: 1
2020-08-18 18:44:59.429 DEBUG 3819 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : ==> Preparing: SELECT user_id,user_name,password,status,create_time,update_time,create_user_id,update_user_id FROM sys_user WHERE user_id=?
2020-08-18 18:44:59.448 DEBUG 3819 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : ==> Parameters: c32ad96f-ff9f-4642-968c-89ef0a6fa4e0(String)
2020-08-18 18:44:59.743 DEBUG 3819 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : <== Total: 1
SysUser(userId=c32ad96f-ff9f-4642-968c-89ef0a6fa4e0, userName=user4d0f44a5-8, password=qianlingo, status=1, createTime=Tue Aug 18 18:44:59 CST 2020, updateTime=Tue Aug 18 18:44:59 CST 2020, createUserId=a15b6c54-e137-11ea-97ee-0242ac110002, updateUserId=a15b6c54-e137-11ea-97ee-0242ac110002)
2020-08-18 18:45:50.038 INFO 3824 --- [ main] c.q.mybatisplushandlerdemo.SysUserTest : 开始执行 updateTest 方法
2020-08-18 18:45:50.090 INFO 3824 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-08-18 18:45:50.383 INFO 3824 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-08-18 18:45:50.402 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : ==> Preparing: SELECT user_id,user_name,password,status,create_time,update_time,create_user_id,update_user_id FROM sys_user WHERE user_id=?
2020-08-18 18:45:50.444 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : ==> Parameters: c32ad96f-ff9f-4642-968c-89ef0a6fa4e0(String)
2020-08-18 18:45:50.470 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : <== Total: 1
2020-08-18 18:45:50.612 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.updateById : ==> Preparing: UPDATE sys_user SET user_name=?, password=?, status=?, create_time=?, update_time=?, create_user_id=?, update_user_id=? WHERE user_id=?
2020-08-18 18:45:50.629 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.updateById : ==> Parameters: user4d0f44a5-8(String), qianlingooo(String), 1(Integer), 2020-08-18 18:44:59.0(Timestamp), 2020-08-18 18:45:50.611(Timestamp), a15b6c54-e137-11ea-97ee-0242ac110002(String), a15b6c54-e137-11ea-97ee-0242ac110002(String), c32ad96f-ff9f-4642-968c-89ef0a6fa4e0(String)
2020-08-18 18:45:50.636 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.updateById : <== Updates: 1
2020-08-18 18:45:50.637 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : ==> Preparing: SELECT user_id,user_name,password,status,create_time,update_time,create_user_id,update_user_id FROM sys_user WHERE user_id=?
2020-08-18 18:45:50.637 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : ==> Parameters: c32ad96f-ff9f-4642-968c-89ef0a6fa4e0(String)
2020-08-18 18:45:50.640 DEBUG 3824 --- [ main] c.q.m.m.m.sys.SysUserMapper.selectById : <== Total: 1
SysUser(userId=c32ad96f-ff9f-4642-968c-89ef0a6fa4e0, userName=user4d0f44a5-8, password=qianlingooo, status=1, createTime=Tue Aug 18 18:44:59 CST 2020, updateTime=Tue Aug 18 18:45:51 CST 2020, createUserId=a15b6c54-e137-11ea-97ee-0242ac110002, updateUserId=a15b6c54-e137-11ea-97ee-0242ac110002)
create_time、update_time、create_user_id、update_user_id四个字段均按需求自动填充了!
结尾
文章的相关代码已同步上传至Github和Gitee了,如果对文章有不清楚的地方何不看看代码?:)
GitHub: https://github.com/qianlingo/mybatis-plus-handler-demo
Gitee: https://gitee.com/qianlingooo/mybatis-plus-handler-demo