功能:在新增護理項目的時候,創建人,創建時間和修改時間字段會自動攔截填充,這些公共字段可以省去我們一個一個處理的麻煩
依靠:AutoFillInterceptor攔截器,MybatisConfig配置類
第一步:我們需要借助一個MybatisConfig,@configuration標志著這是一個配置類,我們需要將autoFillInterceptor放在配置類中并用@Bean注解將這個配置類交給spring容器gu
第二步:了解配置類的原理,也就是AutoFillInterceptor,這個攔截器的代碼大部分是固定的我用只需要了解它的邏輯,在需要的時候復制代碼并根據需求修改
package com.zzyl.intercept;// 引入工具類和依賴
import cn.hutool.core.util.ObjectUtil;
import com.zzyl.utils.EmptyUtil;
import com.zzyl.utils.UserThreadLocal;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Properties;// 聲明這是一個MyBatis攔截器,攔截Executor的update方法
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Component // 聲明為Spring組件
public class AutoFillInterceptor implements Interceptor {// 常量定義:字段名private static final String CREATE_BY = "createBy"; // 創建人字段名private static final String UPDATE_BY = "updateBy"; // 更新人字段名private static final String CREATE_TIME = "createTime"; // 創建時間字段名private static final String UPDATE_TIME = "updateTime"; // 更新時間字段名// 攔截邏輯核心方法@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 獲取方法參數:MappedStatement和SQL參數對象Object[] args = invocation.getArgs();MappedStatement ms = (MappedStatement) args[0]; // 獲取SQL映射信息SqlCommandType sqlCommandType = ms.getSqlCommandType(); // 獲取SQL操作類型(INSERT/UPDATE等)Object parameter = args[1]; // 獲取SQL參數對象// 僅當參數和SQL類型有效時處理if (parameter != null && sqlCommandType != null) {Long userId = loadUserId(); // 獲取當前用戶ID(從ThreadLocal)// 處理INSERT操作if (SqlCommandType.INSERT.equals(sqlCommandType)) {// 批量插入場景(參數是ParamMap且包含list字段)if (parameter instanceof MapperMethod.ParamMap) {MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameter;ArrayList list = (ArrayList) paramMap.get("list"); // 獲取批量數據列表list.forEach(v -> {// 為每條數據設置創建人、創建時間、更新時間setFieldValByName(CREATE_BY, userId, v);setFieldValByName(CREATE_TIME, LocalDateTime.now(), v);setFieldValByName(UPDATE_TIME, LocalDateTime.now(), v);});paramMap.put("list", list); // 更新修改后的參數} else {// 單條插入場景setFieldValByName(CREATE_BY, userId, parameter);setFieldValByName(CREATE_TIME, LocalDateTime.now(), parameter);setFieldValByName(UPDATE_TIME, LocalDateTime.now(), parameter);}} // 處理UPDATE操作else if (SqlCommandType.UPDATE.equals(sqlCommandType)) {// 設置更新人和更新時間setFieldValByName(UPDATE_BY, userId, parameter);setFieldValByName(UPDATE_TIME, LocalDateTime.now(), parameter);}}// 繼續執行原始SQL操作return invocation.proceed();}/*** 通過反射設置實體對象的字段值* @param fieldName 字段名(如createBy)* @param fieldVal 字段值(如用戶ID)* @param parameter 實體對象*/private void setFieldValByName(String fieldName, Object fieldVal, Object parameter) {// 使用MyBatis的MetaObject操作對象屬性MetaObject metaObject = SystemMetaObject.forObject(parameter);// 如果是createBy字段且已有值,則跳過(避免覆蓋)if (fieldName.equals(CREATE_BY)) {Object value = metaObject.getValue(fieldName);if (ObjectUtil.isNotEmpty(value)) {return;}}// 如果字段存在setter方法,則設置值if (metaObject.hasSetter(fieldName)) {metaObject.setValue(fieldName, fieldVal);}}// 包裝目標對象(Executor),返回代理對象@Overridepublic Object plugin(Object target) {if (target instanceof Executor) {return Plugin.wrap(target, this); // MyBatis提供的包裝方法}return target; // 非Executor類型直接返回}// 可讀取配置文件屬性(本例未使用)@Overridepublic void setProperties(Properties properties) {}/*** 獲取當前用戶ID(優先級:ThreadLocal用戶ID → 管理用戶ID → 默認值1)*/public static Long loadUserId() {Long userId = UserThreadLocal.getUserId(); // 從業務線程獲取if (ObjectUtil.isNotEmpty(userId)) {return userId;}userId = UserThreadLocal.getMgtUserId(); // 從管理線程獲取if (!EmptyUtil.isNullOrEmpty(userId)) {return userId;}return 1L; // 默認值}
}