下面将为你详细介绍如何通过自定义 MyBatis 多拦截器协作,在 XML 文件中为 SQL 添加过滤条件。我们会创建两个拦截器,一个用于在 SQL 执行前解析并添加过滤条件,另一个用于打印添加条件后的 SQL 信息。
实现思路
-
自定义拦截器 :创建两个实现
Interceptor
接口的拦截器类。第一个拦截器用于在 SQL 中添加过滤条件,第二个拦截器用于打印添加条件后的 SQL 信息。 -
注册拦截器 :将这些拦截器注册到 MyBatis 的
InterceptorChain
中。 -
定义拦截顺序:按照业务需求,确定各个拦截器的执行顺序。
代码示例
第一个拦截器,在 SQL 中添加过滤条件 AddFilterConditionInterceptor
java
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.Connection;
import java.util.Properties;
// 第一个拦截器,在 SQL 中添加过滤条件
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class AddFilterConditionInterceptor implements Interceptor {
private static final String FILTER_CONDITION = " AND status = 'ACTIVE'";
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
BoundSql boundSql = statementHandler.getBoundSql();
String originalSql = boundSql.getSql();
// 检查 SQL 是否为 SELECT 语句
if (originalSql.trim().toUpperCase().startsWith("SELECT")) {
String newSql = originalSql + FILTER_CONDITION;
metaObject.setValue("delegate.boundSql.sql", newSql);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 设置属性
}
}
第二个拦截器,打印添加条件后的 SQL 信息 PrintSqlInterceptor
java
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.util.Properties;
// 第二个拦截器,打印添加条件后的 SQL 信息
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class PrintSqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
System.out.println("Modified SQL: " + sql);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 设置属性
}
}
MyBatis 配置类,注册拦截器
java
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
// MyBatis 配置类,注册拦截器
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// 创建拦截器实例
AddFilterConditionInterceptor addFilterConditionInterceptor = new AddFilterConditionInterceptor();
PrintSqlInterceptor printSqlInterceptor = new PrintSqlInterceptor();
// 获取 MyBatis 配置
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// 添加拦截器
configuration.addInterceptor(addFilterConditionInterceptor);
configuration.addInterceptor(printSqlInterceptor);
sessionFactory.setConfiguration(configuration);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sessionFactory.getObject();
}
}
代码解释
AddFilterConditionInterceptor
类:
- 该拦截器在 SQL 执行前进行拦截。
- 通过
MetaObject
获取原始 SQL 语句。 - 检查 SQL 是否为
SELECT
语句,如果是,则在原 SQL 后面添加过滤条件AND status = 'ACTIVE'
。 - 使用
metaObject.setValue
方法更新 SQL 语句。
PrintSqlInterceptor
类:
- 该拦截器在 SQL 执行前进行拦截。
- 获取添加过滤条件后的 SQL 语句并打印。
MyBatisConfig
类:
-
这是 MyBatis 的配置类,通过
SqlSessionFactoryBean
创建 SqlSessionFactory
。 -
在配置中添加自定义的拦截器,拦截器的添加顺序即为执行顺序。
注意事项
- 拦截器的执行顺序由添加到
Configuration
中的顺序决定。 - 过滤条件的添加逻辑可以根据实际需求进行修改,例如从上下文或参数中获取动态条件。
- 多个拦截器之间要避免逻辑冲突,以免影响系统的正常运行。
- 拦截器的逻辑要尽量简洁,避免影响 SQL 的执行性能。