MyBatis-Plus 拦截器
MyBatis-Plus 是一个基于 MyBatis 的增强工具,旨在简化数据库操作的复杂性,提高开发效率。在 MyBatis-Plus 中,拦截器是一种强大的功能,允许你在 SQL 执行前后进行自定义的操作。本文将详细探讨 MyBatis-Plus 拦截器的工作原理、使用方法以及应用场景。
一、什么是 MyBatis-Plus 拦截器?
MyBatis-Plus 拦截器是一个实现了 MyBatis 拦截器接口的组件,可以在 MyBatis 的执行过程中拦截和处理 SQL 语句。它允许开发者在 SQL 执行前后插入自定义的逻辑,例如记录日志、修改 SQL 语句等。
二、MyBatis-Plus 拦截器的工作原理
MyBatis-Plus 拦截器的核心是 MyBatis 提供的 Interceptor
接口。开发者可以通过实现 Interceptor
接口来创建自定义的拦截器。MyBatis 在执行 SQL 语句时,会调用这些拦截器的相应方法,从而实现自定义的操作。
拦截器的工作流程如下:
- 注册拦截器:在 MyBatis 配置文件中或通过 Spring Boot 的配置文件中注册自定义拦截器。
- 拦截 SQL 执行:拦截器会在 SQL 执行的过程中被调用,你可以在这些拦截器中实现自定义逻辑。
- 执行后处理:拦截器可以在 SQL 执行后对结果进行处理,或者在 SQL 执行前对 SQL 进行修改。
1. MyBatis 拦截器机制概述
MyBatis 的拦截器机制允许开发者在 MyBatis 的执行流程中插入自定义逻辑。MyBatis 的拦截器可以拦截四个主要的组件:
- Executor:负责执行 SQL 语句的组件。
- StatementHandler:负责准备 SQL 语句并将其传递给数据库的组件。
- ParameterHandler:负责将参数设置到 SQL 语句中的组件。
- ResultSetHandler:负责处理查询结果集的组件。
拦截器通过实现 Interceptor
接口,并在 @Signature
注解中指定要拦截的方法,可以拦截这些组件的操作方法。
2. MyBatis 拦截器的生命周期
- 拦截器注册 :在 MyBatis 配置文件中或通过
SqlSessionFactory
配置时注册拦截器。 - 拦截器实例化 :MyBatis 会创建拦截器实例,并通过
plugin
方法对目标对象进行包装。 - 方法拦截 :在目标方法执行前后,拦截器可以插入自定义逻辑。在执行
intercept
方法时,可以通过Invocation
对象访问目标方法的信息。 - 目标方法执行 :拦截器在执行自定义逻辑后,通常会调用目标方法的
proceed
方法,确保原始功能得以保留。 - 拦截器链:多个拦截器可以链式调用,依次拦截目标方法。
三、如何使用 MyBatis-Plus 拦截器
1. 创建自定义拦截器
首先,你需要实现 Interceptor
接口。以下是一个简单的自定义拦截器的示例:
java
import org.apache.ibatis.executor.statement.StatementHandler;
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 java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyCustomInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取 StatementHandler 对象
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
// 打印 SQL 语句
System.out.println("Intercepted SQL: " + statementHandler.getBoundSql().getSql());
// 执行原始的处理过程
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Interceptor.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可以通过 properties 设置一些属性
}
}
2. 注册拦截器
自定义拦截器的注册可以通过 MyBatis 配置文件或 Spring Boot 配置类进行
java
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.google.common.collect.Lists;
import com.aa.bb.cc.dd.test.handler.QueryStatisticsInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
@Slf4j
@Configuration
public class OPPConfiguration implements EnvironmentAware,BeanFactoryAware{
@Autowired(required = false)
private DataSource dataSource;
private BeanFactory beanFactory;
@Bean("sqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(this.dataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:com/aa/bb/cc/**/mapper/**/**.xml"));
sqlSessionFactoryBean.setVfs(SpringBootVFS.class);
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();
List<InnerInterceptor> interceptors = Lists.newArrayList(interceptor.getInterceptors());
interceptors.clear();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
// 分页插件
interceptors.add(paginationInnerInterceptor);
// 乐观锁插件
interceptors.add(new OptimisticLockerInnerInterceptor());
interceptor.setInterceptors(interceptors);
GlobalConfig globalConfig = GlobalConfigUtils.defaults();
GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();
globalConfig.setDbConfig(dbConfig);
globalConfig.setMetaObjectHandler(new OpaMetaObjectHandler());
globalConfig.setSqlInjector(new EasySqlInjector());
sqlSessionFactoryBean.setGlobalConfig(globalConfig);
sqlSessionFactoryBean.getObject().getConfiguration().addInterceptor(interceptor);
//注册自定义拦截器
sqlSessionFactoryBean.getObject().getConfiguration().addInterceptor(new QueryStatisticsInterceptor());
SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBean.getObject();
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
ClassPathMapperScanner scanner = new ClassPathMapperScanner((BeanDefinitionRegistry) beanFactory);
scanner.setSqlSessionTemplate(sqlSessionTemplate);
scanner.registerFilters();
scanner.scan("com.aa.bb.cc.cc.test.dao","com.aa.bb.cc.dao");
return sqlSessionFactoryBean.getObject();
}
3. 使用拦截器
创建了自定义拦截器并注册后,当 SQL 执行时,MyCustomInterceptor
将拦截并打印 SQL 语句。你可以根据需要在 intercept
方法中实现更复杂的逻辑,如修改 SQL、记录日志等。
四、常见拦截器应用场景
- SQL 日志记录:记录所有执行的 SQL 语句及其执行时间,以便进行性能监控和调试。
- 动态 SQL 修改:在执行前动态修改 SQL 语句,如添加调试信息或修改查询条件。
- 性能监控:记录 SQL 执行时间,分析性能瓶颈。
- 权限控制:根据用户权限动态修改 SQL 查询。
五、常见 MyBatis Plus 拦截器
- 分页拦截器
分页拦截器 PaginationInterceptor
是 MyBatis Plus 提供的一个重要拦截器,用于处理分页查询。它会自动识别分页参数,并在 SQL 中添加相应的分页条件。
- 乐观锁拦截器
乐观锁拦截器 OptimisticLockerInterceptor
用于处理乐观锁机制,确保在并发更新时数据的一致性。它会在 SQL 中添加 version
字段,以支持乐观锁的检查和更新。
- 性能分析拦截器
性能分析拦截器 PerformanceInterceptor
用于打印 SQL 执行的性能分析信息,包括 SQL 执行时间等。它帮助开发者识别性能瓶颈和优化 SQL 查询。
六、注意事项
- 性能影响:拦截器的逻辑会增加额外的开销,尤其是在复杂的拦截器中,因此要注意性能影响。
- 调试与测试:在使用拦截器时,要确保对其进行充分的测试,以避免引入潜在的 bug。
- 兼容性:不同版本的 MyBatis 和 MyBatis-Plus 可能对拦截器的支持有所不同,确保使用兼容的版本。
结语
MyBatis-Plus 拦截器为开发者提供了强大的自定义能力,可以在 SQL 执行过程中插入各种业务逻辑。通过合理使用拦截器,可以大大提升开发效率和系统性能。希望本文对你了解和使用 MyBatis-Plus 拦截器有所帮助。