问题描述
MyBatisPlus公共自动填充,也就是在插入或更新的时候为指定字段赋予指定的值,常见的例子就是更新时间一般都是现在的时间,就可以将这个字段进行统一处理,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码
解决方案:
实现步骤:
1、在实体类的属性加入@TableField注解,指定自动生成的策略
下面以员工实体类为例,模仿如何使用即可
/**
* 员工实体类
*/
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;//身份证号码
private Integer status;
@TableField(fill = FieldFill.INSERT)//插入时自动填充字段
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)//插入和更新时填充字段
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)//插入时自动填充字段
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)//插入和更新时填充字段
private Long updateUser;
}
2、按照MyBatisPlus框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandle接口。
创建该类一般放在common包下,就是公共包下
代码如下:
/**
* 自定义元数据处理器
* @author mrs
* @create 2022-06-01 16:13
*/
@Component//使用Spring框架管理
@Slf4j//日志
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 该方法指向插入语句时执行
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充[insert...]");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());//创建时间统一赋值
metaObject.setValue("updateTime", LocalDateTime.now());//更新时间统一赋值
metaObject.setValue("createUser",new Long(1));//创建人暂时写死
metaObject.setValue("updateUser" ,new Long(1));//更新人暂时写死
}
/**
* 该方法指向更新语句时执行
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充[update...]");
log.info(metaObject.toString());
metaObject.setValue("updateTime", LocalDateTime.now());//更新时间统一赋值
metaObject.setValue("updateUser" ,new Long(1));//更新人暂时写死
}
}
完善上面的写死的代码
有时在元数据对象处理器中需要得到对应的用户session的id,所以扩展知识:就是客户端发送的每次http请求,对应的在服务器都会分配一个新的线程来处理,在处理过程中涉及的公共字段和创建修改的方法都是属于相同的线程
这个时候就需要ThreadLocal
什么是ThreadLocal?
ThreadLocal并不是一个Thread,而是Thread的局部变量。使用ThreadLocal维护变量时,ThreadLocal为每一个使用该变量的线程提供独立的变量副本,所以每一个线程可以独立的改变自己的副本,而不会影响它线程所对应的副本。
ThreadLocal为每个线程提供单独存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
ThreadLocal常用方法
public void set(T value) 设置当前线程的线程局部变量
public T get() 设置当前线程所对应的线程局部变量的值
所以这里完善代码获取对应的session中的id就使用这个类的线程特性来解决,就是在过滤器中的doFilter方法中(Springboot添加过滤器的设置请看我的CSDN上的记录文章)获取当前登录的用户id,并调用ThreadLocal的set设置当前线程的线程局部变量的值(用户id),然后在上面的元数据处理器中的updateFil方法中调用ThreadLocal的get方法来获取当前线程suo对应的线程局部变量的值(用户id)
完善用户id存储到公共字段填充的问题
实现步骤
1、编写BaseContext工具类存在common(公共)包下,基于ThreadLocal封装的工具类
/**
* 基于ThreadLocal封装的工具类,用户保存和获取当前登录用户的id
* @author mrs
* @create 2022-06-01 17:03
*/
public class BaseContext {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();//这里我的id是long,使用的雪花算法生成的
public void setCurrentId(Long id){
threadLocal.set(id);
}
public static Long getCurrentId(){
return threadLocal.get();
}
}
2、在登录的过滤器的doFilter方法中调用BaseContext来设置当前登录用户的id
//将session的id存入ThreadLocal里面
Long empId = (Long)request.getSession().getAttribute("employee");
BaseContext.setCurrentId(empId);
3、在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id
上面MyMetaObjectHandler类更新如下:
/**
* 自定义元数据处理器
* @author mrs
* @create 2022-06-01 16:13
*/
@Component//使用Spring框架管理
@Slf4j//日志
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 该方法指向插入语句时执行
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充[insert...]");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());//创建时间统一赋值
metaObject.setValue("updateTime", LocalDateTime.now());//更新时间统一赋值
metaObject.setValue("createUser",BaseContext.getCurrentId());//创建人更新人使用ThreadLocal进行传输用户id
metaObject.setValue("updateUser" ,BaseContext.getCurrentId());//更新人更新人使用ThreadLocal进行传输用户id
}
/**
* 该方法指向更新语句时执行
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充[update...]");
log.info(metaObject.toString());
metaObject.setValue("updateTime", LocalDateTime.now());//更新时间统一赋值
metaObject.setValue("updateUser" ,BaseContext.getCurrentId());//更新人使用ThreadLocal进行传输用户id
}
}