Mybatis拦截器记录数据变更信息

目的:业务数据表在有DML语句时,将变更数据记录到历史表中,便于数据回溯。

1.定义拦截器

@Slf4j
//@Component
@Intercepts({
        @Signature(type = Executor.class,
                method = "update",
                args = {MappedStatement.class, Object.class}),
})

public class SelfInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        // 获取MappedStatement
        MappedStatement mappedStatement = (MappedStatement) args[0];
        Object param = null;
        //获取类型
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        if(SqlCommandType.DELETE.equals(sqlCommandType) ||
                SqlCommandType.UPDATE.equals(sqlCommandType) ||
                SqlCommandType.INSERT.equals(sqlCommandType)){
            // 获取SQL语句的ID,哪个方法执行
            String sqlId = mappedStatement.getId();
            if(args.length>=2){
                param = args[1];
            }
//            // 获取SQL语句
//            BoundSql boundSql = mappedStatement.getBoundSql(args[1]);
//            String sql = boundSql.getSql();
        }

        try {
            return invocation.proceed();
        } finally {
            if(param!=null&&SqlUtil.getLocalHistory()) {
                SqlUtil.clearLocalHistory();
                // 变更信息入表
HistoryInsertService.insertIntoHistory(SqlUtil.getTableName(),param);
            }
        }


    }

    @Override
    public Object plugin(Object target) {
        // 使用 Plugin.wrap 方法生成代理对象
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 获取配置文件中的属性值
    }


}

拦截器加入Mybatis configuration

  • Springboot-Mybatis-start可以直接使用@Component加入
  • Spring环境可以在Mybatis配置文件加入
    <plugins>
        <plugin interceptor="com.xxx.SelfInterceptor" />
    </plugins>

2.自定义注解 && 设置上下文环境

(1)注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface History {
    String table();
}

table为业务数据表名

(2)上下文环境

@Aspect
@Component
@Order(2)
public class HistoryAspectConfig {

    @Pointcut("@annotation(com.xxx.History)")
    public void cut() {}

    @Before("cut() && @annotation(history)")
    public void before(History history){
        SqlUtil.setLocalHistory();
        SqlUtil.setTableName(history.table());
    }


    /**
     * 无论什么情况下都会执行的方法
     */
    @After("@annotation(com.xxx.History)")
    public void after(){
        SqlUtil.clearLocalHistory();
        SqlUtil.clearTableName();
    }

}


注解加到Mapper方法上面,在mapper执行前,设置标识符,标记此次方法需要增加
after将Threadlocal清理,确保无内存泄露问题

public class SqlUtil {
    private static ThreadLocal<String> localHistory = new ThreadLocal<>();
    private static ThreadLocal<String> tableName = new ThreadLocal<>();


    public static void setLocalHistory(){
        localHistory.set("Yeah");
    }

    public static void clearLocalHistory(){
        localHistory.remove();
    }

    public static boolean getLocalHistory(){
        return "Yeah".equals(localHistory.get());
    }

    public static void clearTableName(){
        tableName.remove();
    }

    public static void setTableName(String name){
        tableName.set(name);
    }

    public static String getTableName(){
        return tableName.get();
    }
}

localHistory记录标识,用于判断此sql是否需要记录到变更表中
tableName用于记录业务表的名称

3.将业务表变更的数据入库(记录版本)

@Service("historyInsertService")
public class HistoryInsertService {

    private static HistoryTestMapper historyTestMapper;

    @Autowired
    public void setHistoryTestMapper(HistoryTestMapper historyTestMapper) {
        HistoryInsertService.historyTestMapper = historyTestMapper;
    }

    public static void insertIntoHistory(String table,Object data){
        String jsonString = JSON.toJSONString(data);

        // 将 JSON 字符串转换为 Map
        Map<String, Object> jsonMap = JSON.parseObject(jsonString, new TypeReference<Map<String, Object>>() {});
        String id = jsonMap.get("id").toString();
        if(StringUtils.isBlank(id)){
            return;
        }
        HistoryTest historyTest = new HistoryTest();
        historyTest.setData(jsonString);
        historyTest.setTableName(table);
        historyTest.setDataId(id);
        List<HistoryTest> historyTests = historyTestMapper.selectByExample(historyTest);
        if(historyTests==null||historyTests.isEmpty()){
            historyTest.setVersion("1");
        }else {
            historyTest.setVersion(String.valueOf((historyTests.size()+1)));
        }
        historyTestMapper.insert(historyTest);
    }
}

4.注解使用

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值