mybatisPlus|mybatis 拦截器(实现简单多租户 + 整合 mybatis plus 不影响分页插件 )

一。 Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、ParameterHandler、StatementHandler、ResultSetHandler四个类里面的方法。
mybatisPlus|mybatis 拦截器(实现简单多租户 + 整合 mybatis plus 不影响分页插件 )
文章图片

二 。可以拦截四个对象中某个对象中的具体某一个方法

@Intercepts({@Signature( type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) public class MyBatisInterceptor implements Interceptor {/** * 代理对象每次调用的方法,就是要进行拦截的时候要执行的方法。在这个方法里面做我们自定义的逻辑处理 */ @Override public Object intercept(Invocation invocation) throws Throwable { return null; }/** * plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理 * * 当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法 -- Plugin.wrap(target, this) * 当返回的是当前对象的时候 就不会调用intercept方法,相当于当前拦截器无效 */ @Override public Object plugin(Object target) { return null; }/** * 用于在Mybatis配置文件中指定一些属性的,注册当前拦截器的时候可以设置一些属性 */ @Override public void setProperties(Properties properties) {} }

Intercepts的声明可以是多个Signaturetype拦截的是那个类method是类中的那个方法args是该方法的参数

三。先看下源码
package org.apache.ibatis.executor; import java.sql.SQLException; import java.util.List; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.transaction.Transaction; public interface Executor { ResultHandler NO_RESULT_HANDLER = null; int update(MappedStatement var1, Object var2) throws SQLException; List query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException; List query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException; Cursor queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException; List flushStatements() throws SQLException; void commit(boolean var1) throws SQLException; void rollback(boolean var1) throws SQLException; CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4); boolean isCached(MappedStatement var1, CacheKey var2); void clearLocalCache(); void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class var5); Transaction getTransaction(); void close(boolean var1); boolean isClosed(); void setExecutorWrapper(Executor var1); }


package org.apache.ibatis.executor.parameter; import java.sql.PreparedStatement; import java.sql.SQLException; public interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStatement var1) throws SQLException; }

package org.apache.ibatis.executor.statement; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.session.ResultHandler; public interface StatementHandler { Statement prepare(Connection var1, Integer var2) throws SQLException; void parameterize(Statement var1) throws SQLException; void batch(Statement var1) throws SQLException; int update(Statement var1) throws SQLException; List query(Statement var1, ResultHandler var2) throws SQLException; Cursor queryCursor(Statement var1) throws SQLException; BoundSql getBoundSql(); ParameterHandler getParameterHandler(); }

package org.apache.ibatis.executor.resultset; import java.sql.CallableStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import org.apache.ibatis.cursor.Cursor; public interface ResultSetHandler { List handleResultSets(Statement var1) throws SQLException; Cursor handleCursorResultSets(Statement var1) throws SQLException; void handleOutputParameters(CallableStatement var1) throws SQLException; }

各个接口中的实现功能大概就和table描述一致我们只要根据业务去针对性的进行拦截处理就可以


基于mybatis plus写的一个简单的租户分割
@Slf4j @Intercepts({@Signature( type = StatementHandler .class, method = "prepare", args = {Connection.class, Integer.class})}) public class MyBatisInterceptor extends AbstractSqlParserHandler implements Interceptor {public MyBatisInterceptor() { }/** * 代理对象每次调用的方法,就是要进行拦截的时候要执行的方法。在这个方法里面做我们自定义的逻辑处理 * * * 等于是重写这个方法了 把参数拿过来然后返回原方法的result * * */ @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget()); //映射工具 MetaObject metaObject = SystemMetaObject.forObject(statementHandler); //中间去判断多层 this.sqlParser(metaObject); MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement"); //要是查询操作并且 要求 StatementType 非存储过程(属于jDBCStatement(非预编译) prepareStatement(预编译) CallableStatement 调用存储过程的statement) if(SqlCommandType.SELECT == mappedStatement.getSqlCommandType() && StatementType.CALLABLE != mappedStatement.getStatementType()){ //获取到执行的sql BoundSql boundSql = (BoundSql)metaObject.getValue("delegate.boundSql"); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String tentId = request.getHeader("tentId"); String originalSql = boundSql.getSql(); //可能出现sql 注入验证下参数 metaObject.setValue("delegate.boundSql.sql",originalSql +"and tent_id = " +tentId); } return invocation.proceed(); }/** * plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理 * * 当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法 -- Plugin.wrap(target, this) * 当返回的是当前对象的时候 就不会调用intercept方法,相当于当前拦截器无效 */ @Override public Object plugin(Object target) { return target instanceof StatementHandler ? Plugin.wrap(target, this) : target; }/** * 用于在Mybatis配置文件中指定一些属性的,注册当前拦截器的时候可以设置一些属性 */ @Override public void setProperties(Properties properties) {} }

声明时候bean的加载顺序决定 plugin的加载顺序,在springboot + mybatis plus中我的写法
/** * @author chenkang */ @Configuration @MapperScan(value = https://www.it610.com/article/{"com.chenkang.tenant.mapper"}) public class MybatisPlusConfig {@Bean @Order(1) public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }@Bean @Order(2) public MyBatisInterceptor myBatisInterceptor() { return new MyBatisInterceptor(); }}





【mybatisPlus|mybatis 拦截器(实现简单多租户 + 整合 mybatis plus 不影响分页插件 )】

    推荐阅读