Mybatis 拦截器自动增加审计信息

11 篇文章 0 订阅
5 篇文章 0 订阅

参考 https://www.cnblogs.com/qingshan-tang/p/13299701.html

1. 继承 BaseAudit 基类

@Data
public class BaseAudit {
    /**
     * 创建者
     */
    private String createBy;

    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

    /**
     * 更新者
     */
    private String updateBy;

    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
}

2. 编写 BaseAuditIntercepter

import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.domain.BaseAudit;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
@Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}))
public class BaseAuditInterceptor implements Interceptor {
    private Map<Class, Method> clazz2Id = new ConcurrentHashMap<>();
    private Method noMethod = BaseAuditInterceptor.class.getMethods()[0];
    @Override
    public Object intercept(Invocation invocation) throws IllegalAccessException, InvocationTargetException {
        fillField(invocation);
        return invocation.proceed();
    }

    private void fillField(Invocation invocation) {
        Object[] args = invocation.getArgs();
        if (args.length < 2) {
            return;
        }
        // 参数1 执行语句
        MappedStatement ms = (MappedStatement) args[0];
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        // 如果是“增加”或“更新”操作,则继续进行默认操作信息赋值。否则,则退出
        if (sqlCommandType != SqlCommandType.INSERT && sqlCommandType != SqlCommandType.UPDATE) {
            return;
        }

        // 参数2 参数
        Object parameter = args[1];
        // 插入或更新参数可能是 list 或 实体
        if (parameter instanceof List) {
            List real = (List) parameter;
            if (!(real.get(0) instanceof BaseAudit)) {
                return;
            }
            dealList(real);
        } else if (!(parameter instanceof BaseAudit)) {
            return;
        }
        dealItem((BaseAudit) parameter);
    }

    private void dealItem(BaseAudit parameter) {
        Class<?> c = parameter.getClass();
        Method idMethod;
        try {
            if (!clazz2Id.containsKey(c)) {
                try {
                    idMethod = getIdMethod(c);
                    clazz2Id.put(c, idMethod);
                } catch (Exception e) {
                    clazz2Id.put(c, noMethod);
                    return;
                }
            } else if (clazz2Id.get(c) == noMethod) {
                log.error("没有 id 的方法,请添加 @MybatisId注解,或者增加 id 属性");
                return;
            } else {
                idMethod = clazz2Id.get(c);
            }

            Object id = idMethod.invoke(parameter);
            LocalDateTime time = LocalDateTime.now();
            String userId = getUserId();
            if (id == null) {
                if (parameter.getCreateTime() == null) {
                    parameter.setCreateTime(time);
                }
                if (parameter.getCreateBy() == null) {
                    parameter.setCreateBy(userId);
                }
            } else {
                parameter.setUpdateBy(userId);
                parameter.setUpdateTime(time);
            }
        } catch (Exception e) {
            log.error("处理 审计参数失败 {}", parameter, e);
        }
    }

    private String getUserId() {
        try {
            return SecurityUtils.getLoginUser().getUsername();
        } catch (ServiceException e) {
            log.debug("获取当前线程 userId 失败!", e);
            return "-1";
        }
    }

    /**
     * 获取某个类的 id 字段的 get 方法
     *
     * @param c 类
     * @return id get 方法
     */
    private Method getIdMethod(Class c) throws NoSuchMethodException, NoSuchFieldException {
        // 有 @Id 注解
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(MybatisId.class) != null) {
                try {
                    String camel = StrUtil.upperFirst(StrUtil.toCamelCase(field.getName()));
                    return c.getMethod("get" + camel);
                } catch (NoSuchMethodException e) {
                    log.error("@MybatisId 注解的属性 {},必须有 对应的get 方法", field.getName());
                    throw e;
                }
            }
        }
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            if (method.getAnnotation(MybatisId.class) != null) {
                if (method.getParameterCount() > 0) {
                    log.error("@MybatisId 注解的方法 {} 必须无参数!", method.getName());
                    throw new RuntimeException("@MybatisId 注解的方法必须无参数: " + method.getName());
                }
                return method;
            }
        }
        // 有 id 字段
        Field idFiled = c.getDeclaredField("id");
        if (idFiled != null) {
            try {
                return c.getMethod("getId");
            } catch (NoSuchMethodException e) {
                log.error("@MybatisId 注解的属性 {},必须有 对应的get 方法", idFiled.getName());
                throw e;
            }
        }
        // 抛出异常
        throw new RuntimeException("无 id 方法");
    }

    private void dealList(List<? extends BaseAudit> real) {
        real.forEach(this::dealItem);
    }
}

2.2 与spring 继承


    @Bean
    public String myInterceptor(SqlSessionFactory sqlSessionFactory) {
        sqlSessionFactory.getConfiguration().addInterceptor(new BaseAuditInterceptor());
        return "interceptor";
    }

3. 测试

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = RuoYiApplication.class)
public class ServiceTest {
    @Autowired
    private UserBasicInfoMapper userBasicInfoMapper;

    @Test
    public void test1() {
        UserBasicInfo u = new UserBasicInfo();
        userBasicInfoMapper.insert(u);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值