SpringBoot集成Mybatis自定义拦截器,实现拼接sql和修改(一)

一、应用场景

1.分页,如com.github.pagehelper的分页插件实现;

2.拦截sql做日志监控;

3.统一对某些sql进行统一条件拼接,类似于分页。

二、MyBatis的拦截器简介

然后我们要知道拦截器拦截什么样的对象,拦截对象的什么行为,什么时候拦截?

       在Mybatis框架中,已经给我们提供了拦截器接口,防止我们改动源码来添加行为实现拦截。说到拦截器,不得不提一下,拦截器是通过动态代理对Mybatis加入一些自己的行为。

拦截对象

       确立拦截对象范围:要拦对人,既要保证拦对人,又要保证对正确的人执行正确的拦截动作

拦截地点

       在Mybatis的源码中

拦截时机

        如果mybatis为我们提供了拦截的功能,我们应该在Mybatis执行过程的哪一步拦截更加合适呢?

拦截某个对象干某件事的时候,拦截的时机要对,过早的拦截会耽误别人做自己的工作,拦截太晚达不到目的。

    Mybatis实际也是一个JDBC执行的过程,只不过被包装起来了而已,我们要拦截Mybatis的执行,最迟也要在获取PreparedStatement时拦截:

PreparedStatementstatement=conn.prepareStatement(sql.toString());

在此处偷偷的将sql语句换掉,就可以改变mybatis的执行,加入自己想要的执行行为。

 而获取Mybatis的Statement是在StatementHandler中进行的。

三、代码示例

第一步、引入依赖

        
			org.mybatis.spring.boot
			mybatis-spring-boot-starter
			1.3.2
		

 第二步、配置application.propertities,指定好好映射文件和实体类的目录

### mybatis
mybatis.mapperLocations: classpath:mapping/*.xml  
###classpath就是应用程序resources的路径
mybatis.type-aliases-package: com.pingan.yc.demo.model

第三步、配置代码生成器,引入配置在pom中添加一下代码


		
			
				org.apache.maven.plugins
				maven-compiler-plugin
				
					1.8
					1.8
				
			
			
			
				org.springframework.boot
				spring-boot-maven-plugin
			
			
			
				org.mybatis.generator
				mybatis-generator-maven-plugin
				1.3.2
				
					
					${basedir}/src/main/resources/generator/generatorConfig.xml
					true
					true
				
			
		
	

在resource文件夹下新建generator.xml文件


SpringBoot集成Mybatis自定义拦截器,实现拼接sql和修改(一)_第1张图片

内容如下:需要注意配置数据库连接驱动和数据库连接,指定生成实体类和映射文件的路径,最后按格式配置要生成的数据表




    
    
    
        
            
            
            
        
        
        
        
        
            
        
        
        
            
            
        
        
        
            
        
        
        
            
        
        
        

最后:在maven命令窗口或如下界面中执行mybatis-generator:generate命令

SpringBoot集成Mybatis自定义拦截器,实现拼接sql和修改(一)_第2张图片

最后生成如下文件:

SpringBoot集成Mybatis自定义拦截器,实现拼接sql和修改(一)_第3张图片

第四步、拦截器 

(生成对应文件后,mybatis环境算是集成好了,可以运行一个测试类试试能否从数据库读取数据。)

定义一个类实现Mybatis的Interceptor接口,@Component注解必须要添加,不然可能出现拦截器无效的情况!!!

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;


@Component
@Intercepts({
        @Signature(
                type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class
        })
})

public class MySqlInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {


        // 方法一
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        //先拦截到RoutingStatementHandler,里面有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,然后就到BaseStatementHandler的成员变量mappedStatement
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        //id为执行的mapper方法的全路径名,如com.uv.dao.UserMapper.insertUser
        String id = mappedStatement.getId();
        //sql语句类型 select、delete、insert、update
        String sqlCommandType = mappedStatement.getSqlCommandType().toString();
        BoundSql boundSql = statementHandler.getBoundSql();

        //获取到原始sql语句
        String sql = boundSql.getSql();
        String mSql = sql;
        //TODO 修改位置

        //注解逻辑判断  添加注解了才拦截
        Class classType = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf(".")));
        String mName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
        for (Method method : classType.getDeclaredMethods()) {
            if (method.isAnnotationPresent(InterceptAnnotation.class) && mName.equals(method.getName())) {
                InterceptAnnotation interceptorAnnotation = method.getAnnotation(InterceptAnnotation.class);
                if (interceptorAnnotation.flag()) {
                    mSql = sql + " limit 2";
                }
            }
        }

        //通过反射修改sql语句
        Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, mSql);
        return invocation.proceed();

    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }

    }

        @Override
    public void setProperties(Properties properties) {

    }
}

此外,定义了一个方法层面的注解,实现局部指定拦截


@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface InterceptAnnotation {
    boolean flag() default  true;
}

最后只需要在指定的方法出添加注解就可以实现局部拦截

SpringBoot集成Mybatis自定义拦截器,实现拼接sql和修改(一)_第4张图片

最后运行看结果就好了。(按我的逻辑下来会只查到2条数据)

在@Interceptor的注解中也有以下的注解方式,究竟有什么不同和差异,请大家自己研究咯,我就在此抛砖引玉了,请各位大牛指导了。

@Intercepts(value = {
        @Signature(type = Executor.class,
                method = "update",
                args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,
                        CacheKey.class, BoundSql.class}),
        @Signature(type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})

 

 

 

你可能感兴趣的:(Mybatis应用)