首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

MyBatis 攔截器實戰:自動填充實體操作審計欄位

     編輯:Java 相關知識

場景痛點

資料表常包含 `created_by`、`created_time`、`updated_by`、`updated_time` 等審計欄位。若在每個 Service 手動設值,不僅繁瑣易漏,還汙染業務邏輯。MyBatis 攔截器可在 SQL 執行前統一攔截,自動填充這些通用欄位。

攔截器實現

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class AuditFieldInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        
        // args[0] = MappedStatement,判斷是 INSERT/UPDATE
        MappedStatement ms = (MappedStatement) args[0];
        SqlCommandType type = ms.getSqlCommandType();
        if (type != SqlCommandType.INSERT && type != SqlCommandType.UPDATE) {
            return invocation.proceed();
        }

        // args[1] = 參數物件(可能是實體、Map、@Param 包裝的 Map)
        Object param = args[1];
        processParameter(param);
        
        return invocation.proceed();
    }

    private void processParameter(Object param) {
        if (param instanceof Map) {
            // @Param 多參數或原始 Map
            for (Object val : ((Map) param).values()) {
                fillAuditFields(val);
            }
        } else {
            // 單一實體物件
            fillAuditFields(param);
        }
    }

    private void fillAuditFields(Object entity) {
        try {
            // 使用 BeanUtils 靜默設值,避免無 setter 報錯
            BeanUtils.setProperty(entity, "createrUsername", getCurrentUser());
            BeanUtils.setProperty(entity, "createDT", new Date());
            BeanUtils.setProperty(entity, "updaterUsername", getCurrentUser());
            BeanUtils.setProperty(entity, "updateDT", new Date());
        } catch (Exception e) {
            // 忽略無對應欄位的實體
        }
    }

    private String getCurrentUser() {
        // 實際專案從 SecurityContext / ThreadLocal 獲取
        return "system";
    }

    @Override public Object plugin(Object target) { return Plugin.wrap(target, this); }
    @Override public void setProperties(Properties properties) {}
}

配置啟用

<!-- mybatis-config.xml 或 Spring Boot @Configuration -->
<plugins>
    <plugin interceptor="com.yourpkg.AuditFieldInterceptor" />
</plugins>

關鍵設計考量

考量點處理策略
參數型別多樣支援單一實體、Map、@Param 包裝 Map,遞迴處理
非全表需審計用 `try-catch` 靜默跳過無審計欄位的實體
效能影響反射設值有微小開銷,核心路徑可接受;大批量匯入建議關閉
事務一致性攔截器在 Executor.update 內,同一事務內多次 update 時間一致

為什麼後來改用 Spring AOP?

作者註記:DAO 層攔截雖可行,但離業務語境太遠(無法獲取當前登入使用者等上下文)。改用 `@Aspect` 切在 Service 層,能自然取得 `SecurityContext`、支援 SpEL 表達式、配合 `@Transactional` 語意更清晰。


MyBatis 攔截器適合「純資料層」的通用邏輯(如分頁、加密、只讀從庫路由)。涉及業務上下文的審計填充,**Spring AOP 在 Service 層才是正解**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved