Spring Boot中使用MyBatis-Plus与ThreadLocal自动填充公共字段
在Spring Boot项目开发中,数据库表的审计字段(如创建人、创建时间、修改人、修改时间)通常需要在每次插入和更新时自动维护。传统的手动赋值方式不仅繁琐且易出错。本文介绍如何通过MyBatis-Plus提供的MetaObjectHandler接口实现公共字段的自动填充,并结合ThreadLocal技术在过滤器中获取当前登录用户ID,解决跨层动态传参的问题,从而实现全自动化的数据维护。
1. 公共字段自动填充功能实现
在业务系统中,员工表或各类管理表中通常包含 create_time、create_user、update_time 和 update_user 等公共字段。若在每个Service方法中手动调用setter进行赋值,将导致大量重复代码。
第1步:分析问题与确定策略
我们需要区分不同操作的赋值时机:createTime仅在插入时赋予当前时间;updateTime在插入和更新时均需赋予当前时间;而用户相关字段则依赖当前上下文中的登录用户信息。通过在实体类属性上配置 @TableField(fill = FieldFill.xxx) 注解,可以明确指定各字段的填充规则。
第2步:编写元数据对象处理器
实现MyBatis-Plus的 MetaObjectHandler 接口,重写 insertFill 和 updateFill 方法,统一处理字段逻辑。此组件由Spring容器管理,拦截器会在执行SQL前自动调用。
首先,确保实体类正确映射字段:
@Data
public class Employee implements Serializable {
private Long id;
// ... 其他字段
@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;
}接着,编写处理器类:
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("开始插入填充...");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "createUser", Long.class, BaseContext.getCurrentId());
this.strictInsertFill(metaObject, "updateUser", Long.class, BaseContext.getCurrentId());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("开始更新填充...");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, "updateUser", Long.class, BaseContext.getCurrentId());
}
}
2. 使用ThreadLocal动态获取当前登录用户
上述代码中调用了 BaseContext.getCurrentId()。在Controller和Service中,我们通常能通过Security Context或Session获取用户信息,但在底层持久层的 MetaObjectHandler 中无法直接访问HTTP会话对象。因此,需要一种跨层传递数据的机制。
第3步:理解ThreadLocal的作用
ThreadLocal 为每个线程提供独立的变量副本。在一次完整的HTTP请求生命周期内,服务端分配的线程是固定的。通过将该线程对应的用户ID存入 ThreadLocal,后续的DAO层或工具类均可通过该线程安全地获取同一份数据。
第4步:封装BaseContext工具类
创建一个静态内部持有 ThreadLocal 的工具类,提供设置和获取当前用户ID的方法:
public class BaseContext {
public static ThreadLocal threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
} 第5步:在过滤器中注入用户ID
在拦截未登录请求的 LoginCheckFilter 中,如果校验通过(用户已登录),则在放行前从 Session 取出用户ID并存入 ThreadLocal:
// LoginCheckFilter 片段
if (request.getSession().getAttribute("employee") != null) {
// 1. 获取登录用户ID
Long empId = (Long) request.getSession().getAttribute("employee");
// 2. 存入ThreadLocal供后续层使用
BaseContext.setCurrentId(empId);
// 放行继续执行
filterChain.doFilter(request, response);
return;
}
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
3. 验证与测试
完成配置后,可删除业务逻辑中关于时间戳和用户ID的手动赋值代码。启动项目,通过前端界面执行新增或修改操作。
最后查询数据库,观察 CREATE_TIME、UPDATE_TIME 以及关联的用户ID是否被正确填充。如果一切正常,说明 MetaObjectHandler 与 ThreadLocal 的协作已成功建立,实现了全链路自动化维护。
以上就是Springboot公共字段填充及ThreadLocal模块改进方案的详细内容,更多关于Springboot 公共字段填充的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













