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

一。 Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、ParameterHandler、StatementHandler、ResultSetHandler四个类里面的方法。 

mybatis 拦截器(实现简单多租户 + 整合 mybatis plus 不影响分页插件 )_第1张图片

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

@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  的声明可以是多个   Signature     type  拦截的是那个类      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 非存储过程  (属于jDBC  Statement(非预编译) 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  的加载顺序,在spring  boot + mybatis plus  中我的写法  

/**
 * @author chenkang
 */
@Configuration
@MapperScan(value = {"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)