mybatis拦截器

业务需求:由于公司业务需要在所有的sql的增删改查中必须包含officeId,业务以officeId做隔离。因此做了一个Mybatis的的过去器。通过拦截sql处理的过程来判断接口sql是否包含officeId,如果不包含则添加officeId。@NoNeedOffice的注解可以添加在Dao的接口类或方法上。用于标识不需要处理的接口。

package com.example.springcloud.provider;

import com.example.springcloud.provider.mybatis.NoNeedOffice;
import com.example.springcloud.provider.mybatis.page.ReflectHelper;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Properties;

/**
 * Created by shiwen on 2017/9/10.
 * 自定义mybatis拦截器
 * 1.可以选择开启关闭  ,在mybatisConfig中可以开启或关闭。默认关闭
 * 2.如果有@NeedOffice注解则关闭,没有注解则默认开启
 * 3.如果用户已经注入了officeId怎么办?如果用户已经注入则,默认使用用户的officeID
 * <p>
 * 4.复杂sql如何处理?多个where条件如何处理?
 * 5.如果sql中没有where条件如何处理
 * <p>
 * #目前最简单的实现方式:1.判断是否有@NoOffice注解
 */

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})
})
public class MyBatisInterceptor implements Interceptor {
    private static final Logger logger = LoggerFactory.getLogger(MyBatisInterceptor.class);

    /**
     * 修改sql,在sql中增加officeId通用业务字段
     *
     * @param invocation
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    @Override
    public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {

        if (invocation.getTarget() instanceof RoutingStatementHandler) {

            //1.获取原始sql语句
            RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget();
            StatementHandler delegate = (StatementHandler) ReflectHelper.getFieldValue(statementHandler, "delegate");

            //2.根据方法名反射,判断是否包含@NoNeedOffice注解,来决定是否需要修改sql语句
            MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getFieldValue(delegate, "mappedStatement");
            String sqlId = mappedStatement.getId();

            //如果没有注解则在sql中增加officeId
            if (!hasNoNeedOfficeAnnotation(sqlId)) {
                //3.修改原始sql语句
                modifySql(delegate.getBoundSql());
            }

        }

        return invocation.proceed();
    }

    /**
     * @param sqlId 方法全路径:类路径+方法名,例如:com.example.Demo.getName
     * @return 是否有 NoNeedOffice注解
     */
    private boolean hasNoNeedOfficeAnnotation(String sqlId) {
        //1.得到类路径和方法路径
        int lastIndexOfDot = sqlId.lastIndexOf(".");
        String className = sqlId.substring(0, lastIndexOfDot);
        String methodName = sqlId.substring(lastIndexOfDot + 1);

        //2.得到类上的注解
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        Annotation[] classAnnotations = clazz.getAnnotations();

        if (containsNoNeedOffice(classAnnotations)) {
            return true;
        }

        //3.得到方法上的注解
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            String name = method.getName();
            if (methodName.equals(name)) {
                Annotation[] methodAnnotations = method.getAnnotations();
                if (containsNoNeedOffice(methodAnnotations)) {
                    return true;
                }
            }
        }

        return false;

    }

    /**
     * 判断注解中是否包含 NoNeedOffice的注解
     * @param classAnnotations
     * @return
     */
    private boolean containsNoNeedOffice(Annotation[] classAnnotations) {
        for (Annotation annotation : classAnnotations) {
            if (annotation instanceof NoNeedOffice) {
                return true;
            }
        }
        return false;
    }

    /**
     *  TODO 此处的逻辑很重要
     * 修改原始sql语句,在sql中增减officeId
     *
     * @param boundSql BoundSql 对象
     */
    private void modifySql(BoundSql boundSql) {
        String officeId = getOfficeId();
        String sql = boundSql.getSql();//获取原来的sql
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

        //判断sql中是否包含where条件,是否包含多个where
        ReflectHelper.setFieldValue(boundSql, "sql", sql);
    }

    /**
     * 获取当前用户的officeId
     *
     * @return officeId
     */
    private String getOfficeId() {
        return "123";
    }

    @Override
    public Object plugin(Object o) {
        logger.info(">>>>>>>>>>>>>>>>>plugin");
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {
        logger.info(">>>>>>>>>>>>>>>>>setProperties");
    }
}
时间: 2024-12-18 13:26:18

mybatis拦截器的相关文章

Mybatis拦截器介绍及分页插件

1.1    目录 1.1 目录 1.2 前言 1.3 Interceptor接口 1.4 注册拦截器 1.5 Mybatis可拦截的方法 1.6 利用拦截器进行分页 1.2     前言 拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法.Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑.打个比方,对于Executor

Mybatis拦截器介绍

拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法.Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑.打个比方,对于Executor,Mybatis中有几种实现:BatchExecutor.ReuseExecutor.SimpleExecutor和CachingExecutor.这个时候如果你觉得这几种实现对于Execu

【Mybatis】1、Mybatis拦截器

MyBatis拦截器原理探究 http://www.cnblogs.com/fangjian0423/p/mybatis-interceptor.html MyBatis 拦截器 (实现分页功能) http://www.cnblogs.com/jethypc/p/5149183.html 由浅入深分析mybatis通过动态代理实现拦截器(插件)的原理 http://zhangbo-peipei-163-com.iteye.com/blog/2033832 使用方法 https://github.

Mybatis拦截器实现分页

本文介绍使用Mybatis拦截器,实现分页:并且在dao层,直接返回自定义的分页对象. 最终dao层结果: public interface ModelMapper { Page<Model> pageByConditions(RowBounds rowBounds, Model record); } 接下来一步一步来实现分页. 一.创建Page对象: public class Page<T> extends PageList<T> { private int page

Mybatis拦截器 mysql load data local 内存流处理

Mybatis 拦截器不做解释了,用过的基本都知道,这里用load data local主要是应对大批量数据的处理,提高性能,也支持事务回滚,且不影响其他的DML操作,当然这个操作不要涉及到当前所load的数据,其中在使用的时候一定要local , 这个命令使用是mysql规定的,否则不加则会认为是服务器本地的文件.这里主要是以流的方式来做处理,这样可以使用内存流,这样就可以避免在某些时候需要生成文件才能导入的多余操作,和IO性能消耗.也可以是应用本地的文件. 注:该做法只试用于存入数据的表,不

【Mybatis】1、Mybatis拦截器学习资料汇总

MyBatis拦截器原理探究 http://www.cnblogs.com/fangjian0423/p/mybatis-interceptor.html [myBatis]Mybatis中的拦截器 http://blog.csdn.net/moshenglv/article/details/52699976 MyBatis 拦截器 (实现分页功能) http://www.cnblogs.com/jethypc/p/5149183.html 由浅入深分析mybatis通过动态代理实现拦截器(插件

Mybatis 拦截器(二)

Mybatis拦截器介绍 拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法.Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑.打个比方,对于Executor,Mybatis中有几种实现:BatchExecutor.ReuseExecutor.SimpleExecutor和CachingExecutor.这个时候如果你觉

mybatis拦截器使用

[toc] mybatis拦截器入门 mybatis 拦截器接口Interceptor Interceptor接口,我们通过这个接口可以自定义我们自己的拦截器,从而实现在拦截的时候写上我们自己的一些逻辑.该接口提供了三个方法, 接口介绍: intercept方法: intercept方法是在进行拦截的时候要执行的方法. plugin方法: 该方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理. setProperties方法: mybatis配置文件中co

MyBatis拦截器原理探究

前言: MyBatis拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClose