本文介绍使用 自定义注解 + 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) {}
}
⚠️ 注意:拦截器中通过 MappedStatement
的 id
获取 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();
}
✅ 优势:
- 注解方式更直观,方法名不用再做约定。
- 可灵活控制哪些 SQL 需要关闭 binlog。
- 与 MyBatis 原生 Mapper 配合良好,改动小。