0
点赞
收藏
分享

微信扫一扫

SpringBoot + MyBatis + MySQL 中动态控制 SQL 是否记录到二进制日志binlog (MyBatis 拦截器自定义注解版)


本文介绍使用 自定义注解 + MyBatis 拦截器 的方式来实现 sql_log_bin 动态控制,这样比用方法名判断更直观和优雅。下面给你完整示例:

1️⃣ 定义自定义注解

import java.lang.annotation.*;

@Target(ElementType.METHOD) // 用在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoBinlog {
}

2️⃣ 修改 MyBatis 拦截器支持注解

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;

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

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        boolean disableBinlog = false;

        try {
            // 获取 Mapper 类和方法名
            String className = ms.getId().substring(0, ms.getId().lastIndexOf("."));
            String methodName = ms.getId().substring(ms.getId().lastIndexOf(".") + 1);
            Class<?> mapperClass = Class.forName(className);

            Method method = mapperClass.getMethod(methodName, ms.getParameterMap().getType().getTypeClass());
            if (method.isAnnotationPresent(NoBinlog.class)) {
                disableBinlog = true;
            }
        } catch (Exception e) {
            // 如果找不到方法或其他异常,默认不禁用 binlog
        }

        Executor executor = (Executor) invocation.getTarget();
        Connection conn = executor.getTransaction().getConnection();

        if (disableBinlog) {
            try (Statement stmt = conn.createStatement()) {
                stmt.execute("SET sql_log_bin=0");
            }
        }

        Object result = invocation.proceed();

        if (disableBinlog) {
            try (Statement stmt = conn.createStatement()) {
                stmt.execute("SET sql_log_bin=1");
            }
        }

        return result;
    }

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

    @Override
    public void setProperties(Properties properties) {}
}

⚠️ 注意:拦截器中通过 MappedStatementid 获取 Mapper 类名和方法名,然后反射判断方法是否加了 @NoBinlog 注解。

3️⃣ 在 Mapper 方法上使用注解

@Mapper
public interface UserMapper {

    @NoBinlog
    @Insert("INSERT INTO user(name) VALUES(#{name})")
    void insertUserWithoutBinlog(User user);

    @Insert("INSERT INTO user(name) VALUES(#{name})")
    void insertUserWithBinlog(User user);
}

  • insertUserWithoutBinlog 执行时不会写入 binlog。
  • insertUserWithBinlog 正常写入 binlog。

4️⃣ 注册拦截器

和之前一样在 Spring Boot 配置类中注册:

@Bean
public SqlLogBinInterceptor sqlLogBinInterceptor() {
    return new SqlLogBinInterceptor();
}

@Bean
public SqlSessionFactory sqlSessionFactory(javax.sql.DataSource dataSource, SqlLogBinInterceptor interceptor) throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setPlugins(interceptor);
    return factoryBean.getObject();
}

优势:

  1. 注解方式更直观,方法名不用再做约定。
  2. 可灵活控制哪些 SQL 需要关闭 binlog。
  3. 与 MyBatis 原生 Mapper 配合良好,改动小。


举报

相关推荐

0 条评论