MyBatis Plus 是 MyBatis 的增强工具,提供了许多便捷的功能来简化开发,其中插件机制是其重要的扩展点之一。InnerInterceptor
是 MyBatis Plus 3.x 中用于实现插件拦截的一个核心接口,允许开发者自定义拦截逻辑,比如修改 SQL、设置参数或处理结果集。
![图片[1]_MyBatis Plus InnerInterceptor 深度解析:自定义拦截器实战_知途无界](https://zhituwujie.com/wp-content/uploads/2025/03/d2b5ca33bd20250317100112.png)
以下是关于 InnerInterceptor
的实现和使用方式的详细讲解。
一、InnerInterceptor 简介
InnerInterceptor
是 MyBatis Plus 提供的一个拦截器接口,主要用于在 SQL 执行的不同阶段插入自定义逻辑。与 MyBatis 的 Interceptor
类似,但更贴合 MyBatis Plus 的设计理念,通常用于分页、多租户、动态表名等场景。
常见的 InnerInterceptor
实现包括:
- 分页插件(
PaginationInnerInterceptor
) - 多租户插件(
TenantLineInnerInterceptor
) - 动态表名插件(
DynamicTableNameInnerInterceptor
)
二、InnerInterceptor 的核心方法
InnerInterceptor
接口定义了以下核心方法:
public interface InnerInterceptor {/*** 在执行 SQL 之前拦截*/boolean beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout);/*** 在执行查询之前拦截(针对 SELECT 语句)*/void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler, BoundSql boundSql) throws SQLException;/*** 在更新操作(INSERT/UPDATE/DELETE)之前拦截*/void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException;/*** 在 SQL 执行完成后拦截*/void afterQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler, BoundSql boundSql, List<Object> resultList) throws SQLException;/*** 清理资源(可选实现)*/void clearLocalCache();}public interface InnerInterceptor { /** * 在执行 SQL 之前拦截 */ boolean beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout); /** * 在执行查询之前拦截(针对 SELECT 语句) */ void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException; /** * 在更新操作(INSERT/UPDATE/DELETE)之前拦截 */ void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException; /** * 在 SQL 执行完成后拦截 */ void afterQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, List<Object> resultList) throws SQLException; /** * 清理资源(可选实现) */ void clearLocalCache(); }public interface InnerInterceptor { /** * 在执行 SQL 之前拦截 */ boolean beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout); /** * 在执行查询之前拦截(针对 SELECT 语句) */ void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException; /** * 在更新操作(INSERT/UPDATE/DELETE)之前拦截 */ void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException; /** * 在 SQL 执行完成后拦截 */ void afterQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, List<Object> resultList) throws SQLException; /** * 清理资源(可选实现) */ void clearLocalCache(); }
开发者可以根据需求实现其中的方法,在 SQL 执行的各个阶段插入自定义逻辑。
三、自定义 InnerInterceptor 实现
以下是一个简单的自定义 InnerInterceptor
示例,用于记录 SQL 执行时间:
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import java.sql.Connection;import java.sql.SQLException;import java.util.List;public class SqlExecutionTimeInterceptor implements InnerInterceptor {@Overridepublic boolean beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {long startTime = System.currentTimeMillis();// 可以通过 ThreadLocal 或其他方式存储开始时间System.out.println("SQL Preparation Start...");// 模拟在 StatementHandler 中设置属性(可选)return true; // 返回 true 表示继续执行,false 表示中断}@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler, BoundSql boundSql) throws SQLException {System.out.println("Executing Query: " + boundSql.getSql());}@Overridepublic void afterQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler, BoundSql boundSql, List<Object> resultList) throws SQLException {long endTime = System.currentTimeMillis();System.out.println("SQL Execution Time: " + (endTime - (System.currentTimeMillis() - /* 假设获取开始时间 */ 1000)) + "ms");// 注意:实际实现中需要合理管理开始时间(如使用 ThreadLocal)}// 其他方法可以留空或根据需求实现@Overridepublic void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {}@Overridepublic void clearLocalCache() {}}import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import java.sql.Connection; import java.sql.SQLException; import java.util.List; public class SqlExecutionTimeInterceptor implements InnerInterceptor { @Override public boolean beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { long startTime = System.currentTimeMillis(); // 可以通过 ThreadLocal 或其他方式存储开始时间 System.out.println("SQL Preparation Start..."); // 模拟在 StatementHandler 中设置属性(可选) return true; // 返回 true 表示继续执行,false 表示中断 } @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { System.out.println("Executing Query: " + boundSql.getSql()); } @Override public void afterQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, List<Object> resultList) throws SQLException { long endTime = System.currentTimeMillis(); System.out.println("SQL Execution Time: " + (endTime - (System.currentTimeMillis() - /* 假设获取开始时间 */ 1000)) + "ms"); // 注意:实际实现中需要合理管理开始时间(如使用 ThreadLocal) } // 其他方法可以留空或根据需求实现 @Override public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {} @Override public void clearLocalCache() {} }import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import java.sql.Connection; import java.sql.SQLException; import java.util.List; public class SqlExecutionTimeInterceptor implements InnerInterceptor { @Override public boolean beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { long startTime = System.currentTimeMillis(); // 可以通过 ThreadLocal 或其他方式存储开始时间 System.out.println("SQL Preparation Start..."); // 模拟在 StatementHandler 中设置属性(可选) return true; // 返回 true 表示继续执行,false 表示中断 } @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { System.out.println("Executing Query: " + boundSql.getSql()); } @Override public void afterQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, List<Object> resultList) throws SQLException { long endTime = System.currentTimeMillis(); System.out.println("SQL Execution Time: " + (endTime - (System.currentTimeMillis() - /* 假设获取开始时间 */ 1000)) + "ms"); // 注意:实际实现中需要合理管理开始时间(如使用 ThreadLocal) } // 其他方法可以留空或根据需求实现 @Override public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {} @Override public void clearLocalCache() {} }
四、InnerInterceptor 的注册
自定义的 InnerInterceptor
需要通过 MyBatis Plus 的配置类注册到插件中。以下是注册示例:
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 添加自定义 InnerInterceptorinterceptor.addInnerInterceptor(new SqlExecutionTimeInterceptor());// 也可以添加其他内置拦截器,如分页插件// interceptor.addInnerInterceptor(new PaginationInnerInterceptor());return interceptor;}}import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加自定义 InnerInterceptor interceptor.addInnerInterceptor(new SqlExecutionTimeInterceptor()); // 也可以添加其他内置拦截器,如分页插件 // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加自定义 InnerInterceptor interceptor.addInnerInterceptor(new SqlExecutionTimeInterceptor()); // 也可以添加其他内置拦截器,如分页插件 // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }
五、常见内置 InnerInterceptor 的使用
1. 分页插件
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor());return interceptor;}import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; }import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; }
2. 多租户插件
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;import net.sf.jsqlparser.expression.Expression;import net.sf.jsqlparser.expression.StringValue;@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {@Overridepublic Expression getTenantId() {// 返回当前线程的租户 ID(示例)return new StringValue("tenant_id");}@Overridepublic String getTenantIdColumn() {return "tenant_id";}@Overridepublic boolean ignoreTable(String tableName) {// 忽略某些表不添加租户条件return "common_table".equals(tableName);}}));return interceptor;}import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.StringValue; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { @Override public Expression getTenantId() { // 返回当前线程的租户 ID(示例) return new StringValue("tenant_id"); } @Override public String getTenantIdColumn() { return "tenant_id"; } @Override public boolean ignoreTable(String tableName) { // 忽略某些表不添加租户条件 return "common_table".equals(tableName); } })); return interceptor; }import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.StringValue; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { @Override public Expression getTenantId() { // 返回当前线程的租户 ID(示例) return new StringValue("tenant_id"); } @Override public String getTenantIdColumn() { return "tenant_id"; } @Override public boolean ignoreTable(String tableName) { // 忽略某些表不添加租户条件 return "common_table".equals(tableName); } })); return interceptor; }
六、注意事项
- 执行顺序:多个
InnerInterceptor
的执行顺序与添加顺序一致。 - 性能影响:拦截器会增加 SQL 执行的开销,需合理设计逻辑。
- 兼容性:确保自定义拦截器与 MyBatis Plus 版本兼容。
七、总结
InnerInterceptor
是 MyBatis Plus 提供的一个强大扩展点,允许开发者在 SQL 执行的各个阶段插入自定义逻辑。通过实现 InnerInterceptor
接口并注册到 MyBatis Plus 插件中,可以轻松实现分页、多租户、SQL 日志记录等功能。在实际开发中,建议优先使用内置拦截器,必要时再实现自定义逻辑。
暂无评论内容