mybatis-plus自动填充时间的配置类实现
在实际操作过程中,我们并不希望创建时间、修改时间这些来手动进行,而是希望通过自动化来完成,而
mybatis-plus
则也提供了自动填充功能
来实现这一操作,接下来,就来了解一下mybatis-plus
的自动填充功能是如何进行操作的。
文章目录
- mybatis-plus自动填充时间的配置类实现
-
- [1. 目标](#1. 目标)
- [2. 上代码](#2. 上代码)
-
- [2.1. 代码目录](#2.1. 代码目录)
- [2.2. pom文件](#2.2. pom文件)
- [2.4. MyMetaObjectHandler(重点)](#2.4. MyMetaObjectHandler(重点))
- [2.5. MybatisAutoConfiguration](#2.5. MybatisAutoConfiguration)
- [2.6. UserContextHolder](#2.6. UserContextHolder)
- [2.7. BaseEntity](#2.7. BaseEntity)
- [2.8. User](#2.8. User)
- [3. 测试](#3. 测试)
-
- [3.1. insert(User)](#3.1. insert(User))
- [3.2. update(User)](#3.2. update(User))
- [3.3. updateUpdateWrapper()更新失败](#3.3. updateUpdateWrapper()更新失败)
- [3.4. updateUpdateWrapper()更新失败](#3.4. updateUpdateWrapper()更新失败)
- [3.5. userServiceUpdateUpdateWrapper() 更新失败](#3.5. userServiceUpdateUpdateWrapper() 更新失败)
- [3.6. userServiceUpdateLambdaUpdateWrapper() 更新失败](#3.6. userServiceUpdateLambdaUpdateWrapper() 更新失败)
- [4. 改造上面测试更新失败的](#4. 改造上面测试更新失败的)
-
- [4.1. 增加UpdateWapperAspect切面(重点)](#4.1. 增加UpdateWapperAspect切面(重点))
- [4.2. 测试updateUpdateWrapper()更新成功](#4.2. 测试updateUpdateWrapper()更新成功)
- [4.3. 测试updateUpdateWrapper()更新成功](#4.3. 测试updateUpdateWrapper()更新成功)
- [4.4. userServiceUpdateUpdateWrapper() 更新成功](#4.4. userServiceUpdateUpdateWrapper() 更新成功)
- [3.6. userServiceUpdateLambdaUpdateWrapper() 更新成功](#3.6. userServiceUpdateLambdaUpdateWrapper() 更新成功)
- 5.总结
1. 目标
- 实现默认设置创建者id,创建者用户名,更新者id,更新者用户名,创建时间,更新时间
- 实现自定义字段birth_day 没有设置值的时候设置当前时间
- 实现自定义字段login_time没有设置值的时候更新设置当前时间
sql信息如下
sql
drop table if exists liu_user;
CREATE TABLE `liu_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`username` varchar(255) DEFAULT NULL COMMENT '名字',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
`phone` varchar(255) DEFAULT NULL COMMENT '电话号码',
`birth_day` timestamp NULL COMMENT '出生时间',
`login_time` timestamp NULL COMMENT '登录时间',
`create_by` varchar(20) DEFAULT NULL COMMENT '创建者id',
`create_by_name` varchar(100) DEFAULT NULL COMMENT '创建者账号名',
`update_by` varchar(20) DEFAULT NULL COMMENT '更新者id',
`update_by_name` varchar(100) DEFAULT NULL COMMENT '更新者账号名',
`create_time` timestamp NULL COMMENT '创建时间',
`update_time` timestamp NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) COMMENT='用户表';
2. 上代码
2.1. 代码目录
2.2. pom文件
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Tag-单元测试 junit-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- aop切面 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.14.3</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
</dependency>
<!-- Tag-单元测试 junit-->
<!-- Tag-mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- Tag-mybatis plus-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
2.3. yml配置
java
spring:
datasource:
# mysql
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://192.168.0.154:3306/bigdata_src1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
mybatis-plus:
mapper-locations: classpath*:com/liuhm/dao/mysqlmapper/*Mapper.xml
# 实体扫描,多个package用逗号或者分号分隔
type-aliases-package: com.liuhm.entity
global-config:
db-config:
id-type: auto
field-strategy: not_null
logic-delete-value: 0
logic-not-delete-value: 0
banner: false
configuration:
#sql日志打印
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
cache-enabled: false
server:
port: 9099
2.4. MyMetaObjectHandler(重点)
常规实现MetaObjectHandler
java
package com.liuhm.config;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.liuhm.config.context.UserContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.*;
/**
* @ClassName:SmMetaObjectHandler
* @Description: 全局处理基本字段
* @Author: liuhaomin
* @Date: 2023/11/29 15:25
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
private final List<String> userIdFieldList = new ArrayList<>(Arrays.asList("createBy", "updateBy"));
private final List<String> userNameFieldList = new ArrayList<>(Arrays.asList("createByName", "updateByName"));
private final String updateBy = "updateBy";
private final String updateByName = "updateByName";
private final static String JAVA_LANG_OBJECT = "java.lang.object";
@Override
public void insertFill(MetaObject metaObject){
try {
setDefaultValue(metaObject, Arrays.asList(FieldFill.INSERT, FieldFill.INSERT_UPDATE));
} catch (Exception e) {
log.warn("设置默认参数失败 {}");
}
}
@Override
public void updateFill(MetaObject metaObject){
try {
setDefaultValue(metaObject, Arrays.asList(FieldFill.INSERT_UPDATE, FieldFill.UPDATE));
} catch (Exception e) {
log.warn("设置默认参数失败 {}");
}
}
public void setDefaultValue(MetaObject metaObject, List<FieldFill> fieldFills){
List<Field> declaredFields = new ArrayList<>();
Class tempClass = metaObject.getOriginalObject().getClass();
while(tempClass != null && !tempClass.getName().toLowerCase().equals(JAVA_LANG_OBJECT)){
declaredFields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
tempClass = tempClass.getSuperclass();
}
String userId = UserContextHolder.getUserId();
String userName = UserContextHolder.getUserName();
Date date = new Date();
String fieldName = null;
Object val = null;
for(Field declaredField : declaredFields){
fieldName = declaredField.getName();
val = getFieldValByName(fieldName, metaObject);
TableField annotation = declaredField.getAnnotation(TableField.class);
if (Objects.nonNull(annotation) && (fieldFills.contains(annotation.fill()))) {
if (Objects.isNull(val)) {
if (userIdFieldList.contains(fieldName) && Objects.nonNull(userId)) {
val = userId;
}
if (userNameFieldList.contains(fieldName) && Objects.nonNull(userName)) {
val = userName;
}
// 插入的时候符合条件的 为空设置值
// 设置符合条件的默认时间
if ("Date".equals(declaredField.getType().getSimpleName())) {
val = date;
}
} else {
// 更新
if (updateBy.equals(fieldName) && Objects.nonNull(userId)) {
val = userId;
}
if (updateByName.equals(fieldName) && Objects.nonNull(userName)) {
val = userName;
}
}
if(fieldFills.contains(FieldFill.INSERT_UPDATE) && fieldFills.contains(FieldFill.UPDATE)){
// 设置符合条件的默认时间
if ("Date".equals(declaredField.getType().getSimpleName())) {
val = date;
}
}
setFieldValByName(fieldName, val, metaObject);
}
}
}
}
2.5. MybatisAutoConfiguration
java
package com.liuhm.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/**
* mybatis 自动配置类
*
* @author liuhaomin
* @date 2023.11.16
*/
@Configuration
@MapperScan("com.liuhm.dao.mapper*")
public class MybatisAutoConfiguration {
}
2.6. UserContextHolder
java
package com.liuhm.config.context;
import com.alibaba.ttl.TransmittableThreadLocal;
/**
* 模拟用户获取用户名和用户信息 上下文 Holder
*/
public class UserContextHolder {
/**
* 当前用户编号
*/
private static final ThreadLocal<String> USER_ID = new TransmittableThreadLocal<>();
private static final ThreadLocal<String> USER_NAME = new TransmittableThreadLocal<>();
/**
* 获得用户编号。
*
* @return 用户编号
*/
public static String getUserId() {
return USER_ID.get();
}
public static void setUserId(String userId) {
USER_ID.set(userId);
}
public static void clear() {
USER_ID.remove();
USER_NAME.remove();
}
public static String getUserName() {
return USER_NAME.get();
}
public static void setUserName(String userName) {
USER_NAME.set(userName);
}
}
2.7. BaseEntity
java
package com.liuhm.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* @ClassName:BaseEntity
* @Description: 基础的类
* @Author: liuhaomin
* @Date: 2023/11/29 15:17
*/
@Data
public class BaseEntity implements Serializable {
private static final long serialVersionUID = -995832545980280226L;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 最后更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 创建者,目前使用 User 的 id 编号
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
@TableField(fill = FieldFill.INSERT)
private String createByName;
/**
* 更新者,目前使用 User 的 id 编号
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateByName;
/**
* 是否删除
*/
// @TableLogic
// private Boolean deleted;
}
2.8. User
java
package com.liuhm.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @ClassName:User
* @Description: User
* @Author: liuhaomin
* @Date: 2024/12/23 13:50
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "liu_user")
public class User extends BaseEntity {
private static final long serialVersionUID = -1L;
private Integer id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 电话号码
*/
private String phone;
/**
* 出生时间
*/
@TableField(fill = FieldFill.INSERT)
private Date birthDay;
/**
* 登录时间
*/
@TableField(fill = FieldFill.UPDATE)
private Date loginTime;
}
2.9. 其余简单的类
java
public interface UserService extends IService<User>
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService
public interface UserMapper extends BaseMapper<User>
3. 测试
3.1. insert(User)
- 实现默认设置创建者id,创建者用户名,更新者id,更新者用户名,创建时间,更新时间,birth_day 默认有数据
java
@Test
public void insert(){
String userId = "1";
String userName = "admin";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
User user = new User();
user.setUsername("admin");
user.setPassword("123456");
user.setPhone("15723219655");
userMapper.insert(user);
List<User> users = userMapper.selectList(null);
log.info("users:{}", users);
}
3.2. update(User)
- 更新者id,更新者用户名,更新时间,login_time 默认有数据
java
@Test
public void update(){
String userId = "2";
String userName = "admin2";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, "admin"));
user.setPassword("123456789");
userMapper.updateById(user);
List<User> users = userMapper.selectList(null);
log.info("users:{}", users);
}
3.3. updateUpdateWrapper()更新失败
- 更新者id,更新者用户名,更新时间,Password
- 更新失败
java
@Test
public void updateUpdateWrapper(){
String userId = "3";
String userName = "admin3";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
userMapper.update(null, new UpdateWrapper<User>().lambda()
.set(User::getPassword,"963852741")
.eq(User::getUsername, "admin"));
List<User> users = userMapper.selectList(null);
log.info("updateUpdateWrapper:{}", users);
}
3.4. updateUpdateWrapper()更新失败
- 更新者id,更新者用户名,更新时间,Password
- 更新失败
java
@Test
public void updateLambdaUpdateWrapper(){
String userId = "4";
String userName = "admin4";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
userMapper.update(null, new LambdaUpdateWrapper<User>()
.set(User::getPassword,"963852741123465")
.eq(User::getUsername, "admin"));
List<User> users = userMapper.selectList(null);
log.info("updateLambdaUpdateWrapper:{}", users);
}
3.5. userServiceUpdateUpdateWrapper() 更新失败
- 更新者id,更新者用户名,更新时间,Password
- 更新失败
java
@Test
public void userServiceUpdateUpdateWrapper(){
String userId = "5";
String userName = "admin5";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
userService.update( new UpdateWrapper<User>().lambda()
.set(User::getPassword,"963852741")
.eq(User::getUsername, "admin"));
List<User> users = userMapper.selectList(null);
log.info("userServiceUpdateUpdateWrapper:{}", users);
}
3.6. userServiceUpdateLambdaUpdateWrapper() 更新失败
- 更新者id,更新者用户名,更新时间,Password
- 更新失败
java
@Test
public void userServiceUpdateLambdaUpdateWrapper(){
String userId = "6";
String userName = "admin6";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
userService.update( new LambdaUpdateWrapper<User>()
.set(User::getPassword,"963852741123465")
.eq(User::getUsername, "admin"));
List<User> users = userMapper.selectList(null);
log.info("userServiceUpdateLambdaUpdateWrapper:{}", users);
}
4. 改造上面测试更新失败的
4.1. 增加UpdateWapperAspect切面(重点)
java
package com.liuhm.config.acpect;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.liuhm.config.context.UserContextHolder;
import com.liuhm.entity.BaseEntity;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Objects;
/**
* @ClassName:UpdateWapperAspect
* @Description: 解决update(Wrapper updateWrapper),自动填充不生效问题
* @Author: liuhaomin
* @Date: 2024/2/19 9:19
*/
@Aspect
@Component
@Slf4j
public class UpdateWapperAspect{
@Pointcut("execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.update(..))")
public void pointcut(){
}
@Around(value = "pointcut()")
public Object around(ProceedingJoinPoint pjp){
updateEntity(pjp);
try {
return pjp.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
/**
* 重写update(Wrapper<T> updateWrapper), 更新时自动填充不生效问题
*
* @param pjp
*
* @return
*/
private void updateEntity(ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
if (args != null && args.length == 2) {
if(args[0] != null){
return;
}
Object arg = args[1];
String userId = UserContextHolder.getUserId();
String userName = UserContextHolder.getUserName();
if (arg instanceof UpdateWrapper) {
UpdateWrapper updateWrapper = (UpdateWrapper) arg;
String sqlSet = updateWrapper.getSqlSet();
if(!sqlSet.contains("update_time")){
updateWrapper.set("update_time", new Date());
}
if (Objects.nonNull(userId) && !sqlSet.contains("update_by")) {
updateWrapper.set("update_by", userId);
}
if (Objects.nonNull(userName) && !sqlSet.contains("update_by_name")) {
updateWrapper.set("update_by_name", userName);
}
}
if (arg instanceof LambdaUpdateWrapper) {
LambdaUpdateWrapper<BaseEntity> updateWrapper = (LambdaUpdateWrapper) arg;
String sqlSet = updateWrapper.getSqlSet();
if(!sqlSet.contains("update_time")){
updateWrapper.set(BaseEntity::getUpdateTime, new Date());
}
if (Objects.nonNull(userId) && !sqlSet.contains("update_by")) {
updateWrapper.set(BaseEntity::getUpdateBy, userId);
}
if (Objects.nonNull(userName) && !sqlSet.contains("update_by_name")) {
updateWrapper.set(BaseEntity::getUpdateByName, userName);
}
}
}
}
}
4.2. 测试updateUpdateWrapper()更新成功
- 更新者id,更新者用户名,更新时间,Password
java
@Test
public void updateUpdateWrapper(){
String userId = "3";
String userName = "admin3";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
userMapper.update(null, new UpdateWrapper<User>().lambda()
.set(User::getPassword,"963852741")
.eq(User::getUsername, "admin"));
List<User> users = userMapper.selectList(null);
log.info("updateUpdateWrapper:{}", users);
}
4.3. 测试updateUpdateWrapper()更新成功
- 更新者id,更新者用户名,更新时间,Password
java
@Test
public void updateLambdaUpdateWrapper(){
String userId = "4";
String userName = "admin4";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
userMapper.update(null, new LambdaUpdateWrapper<User>()
.set(User::getPassword,"963852741123465")
.eq(User::getUsername, "admin"));
List<User> users = userMapper.selectList(null);
log.info("updateLambdaUpdateWrapper:{}", users);
}
4.4. userServiceUpdateUpdateWrapper() 更新成功
- 更新者id,更新者用户名,更新时间,Password
java
@Test
public void userServiceUpdateUpdateWrapper(){
String userId = "5";
String userName = "admin5";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
userService.update( new UpdateWrapper<User>().lambda()
.set(User::getPassword,"963852741")
.eq(User::getUsername, "admin"));
List<User> users = userMapper.selectList(null);
log.info("userServiceUpdateUpdateWrapper:{}", users);
}
3.6. userServiceUpdateLambdaUpdateWrapper() 更新成功
- 更新者id,更新者用户名,更新时间,Password
java
@Test
public void userServiceUpdateLambdaUpdateWrapper(){
String userId = "6";
String userName = "admin6";
// 设置上下文模拟请求中的用户信息
UserContextHolder.setUserId(userId);
UserContextHolder.setUserName(userName);
userService.update( new LambdaUpdateWrapper<User>()
.set(User::getPassword,"963852741123465")
.eq(User::getUsername, "admin"));
List<User> users = userMapper.selectList(null);
log.info("userServiceUpdateLambdaUpdateWrapper:{}", users);
}
5.总结
实现默认设置创建者id,创建者用户名,更新者id,更新者用户名,创建时间,更新时间
自定义mapperXml sql暂时不会默认更新上述字段
下面的mybatis-plus-fieldfill