瑞吉外卖06-公共字段自动填充
需求分析
问题描述
前面我们已经完成了后台系统的员工管理功能的开发,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工时需要设置修改时间、修改人等字段。
这些字段属于公共字段,也就是也就是在我们的系统中很多表中都会有这些字段。
我们可以先来看看表结构
employee 表
setmeal_dish 表
上述的这两张表都有(创建时间、创建人、修改时间、修改人)字段。
我们之前处理这些字段都是直接对其进行getter、setter操作,例如:
新增员工业务
在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID
编辑员工信息业务
在更新数据时, 将updateTime 设置为当前时间, updateUser设置为当前登录用户ID
如果都按照上述的操作方式来处理这些公共字段,需要在每一个业务方法中进行操作,编码相对冗余、繁琐,那能不能对于这些公共字段在某个地方统一处理,来简化开发呢?
解决方案
对于createTime、updateTime字段
Mybatis Plus 公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码
字段名 | 赋值时机 | 说明 |
---|---|---|
createTime | 插入(INSERT) | 当前时间 |
updateTime | 插入(INSERT) , 更新(UPDATE) | 当前时间 |
createUser | 插入(INSERT) | 当前登录用户ID |
updateUser | 插入(INSERT) , 更新(UPDATE) | 当前登录用户ID |
实现步骤
(1)在实体类的属性上加入 @TableField 注解,指定自动填充的策略。
(2)按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口。
对于createUser、updateUser字段
在自动填充createUser和updateUser时,设置的用户id是动态获取当前登录用户的id
如何获取???
用户登录成功后我们将用户id存入了HttpSession中,所以我们直接从Session对象中获取就行了。
但是,我们在实现MetaObjectHandler接口的类中是不能直接获得HttpSession对象的,所以我们需要通过其他方式来获取登录用户id。
客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程,所以我们自然可以想到用ThreadLocal来解决!!!
代码实现
给employee类加上@TableField注解
编写BaseContext工具类
/**
* 基于ThreadLocal封装的工具类,用于保存、获取当前用户登入的ID
*/
public class BaseContext {
// 作用域是每一个线程之内,每一个请求都是一个新的线程!!!
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
}
注释掉 EmployeeServiceImpl类中的getter、setter方法
在过滤器 LoginCheckFilter类里的session对象中获取员工的ID
在MyMetaObjectHandler中设置值
/**
* MetaObjectHandler 元数据处理器
*/
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入时自动填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("createUser", BaseContext.getCurrentId());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
/**
* 更新时自动填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
}
知识点分析
ThreadLocal
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。
在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
最常用的就是get()、set()、remove()方法了。
具体的ThreadLocal源码分析请见本人的主页的其它文章!