Spring Boot中使用MyBatis-Plus与ThreadLocal自动填充公共字段

橘子海
橘子海
发布时间:2026-10-16 | 2人浏览过
原创

在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 公共字段填充的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表