飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

大白话讲解Mybatis的plugin(Interceptor)的使用

时间:2021-12-04  作者:share2perfect  
Mybatis的plugin Mybatis的interceptor mybatis插件实现脱敏 mybatis根据请求改变sql

        mybatis提供了一个入口,可以让你在语句执行过程中的某一点进行拦截调用。官方称之为插件plugin,但是在使用的时候需要实现Interceptor接口,默认情况下,MyBatis 允许使用插件来拦截的方法调用包括以下四个对象的方法:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

以上内容在官网包括网上一搜一大把,但是用的时候,应该怎么选择,什么时候用哪种,怎么入手呢?

        我一开始想用的时候,也不知道什么时候拦截哪种对象,后来我就写了一个简单的demo,大家在用mybatis的时候,无非就是crud操作,那么我就提供四个plugin,分别来拦截Executor、ParameterHandler、ResultSetHandler、StatementHandler ;然后提供了一个controller暴露了五个接口分别是getUserInfo、listUserInfo、addUser、updateUser、deleteUser,来看下都走了那几个plugin(demo我会上传到码云上,项目架构是springboot+mybatis+mybatis-plus,数据库我用的是postgresql-14),我认为这五个接口涵盖了我们在开发中90%的场景,根据打印的日志得到的结论是:

  1. 两种查询、新增、修改、删除五个方法都会经过StatementHandler、ParameterHandler
  2. 两种查询(单个查询、列表查询)都会经过Executor、StatementHandler、ParameterHandler、ResultSetHandler

所以根据上面的结论,我们就可以来确定我们在开发中用哪种plugin,参考场景如下:

  1. 如果想改入参,比如postgresql据库字段值大小写敏感,那么我可以在ParameterHandler里面获取到入参,然后toUpperCase();
  2. 如果想改sql语句,比如改postgresql的schema,那么我可以在StatementHandler(prepare)里面获取到connection修改;若是查询场景也可以在Executor的query方法中获取connection修改;
  3. 如果想对数据进行脱敏处理,比如查询场景下的,查出的结果中身份证显示前4位后4位中间***填充,那么我们可以在ResultSetHandler的进行脱敏处理。

下面结合代码举两个场景的例子:

场景一:对查询结果数据脱敏处理,首先定义了一个XfactorResultSetHandlerInterceptor,代码如下:

package 域名域名per;

import 域名域名d;
import 域名ement;
import 域名;
import 域名域名域名ngUtils;
import 域名域名域名ltSetHandler;
import 域名域名rceptor;
import 域名域名rcepts;
import 域名域名cation;
import 域名域名ature;
import 域名域名Object;
import 域名域名emMetaObject;
import 域名域名j;

@Slf4j
@Intercepts({
    @Signature(type= 域名s,method = "handleResultSets",args = {域名s})
    })
public class XfactorResultSetHandlerInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        域名("===ResultSetHandler===");
        Object resultSet = 域名eed();
        List resultList = (List)resultSet;
        for(Object item : resultList) {
            Class<?> sourceClass = 域名lass();
            MetaObject metaObject = 域名bject(item);
            Field[] fields = 域名eclaredFields();
            for(Field field : fields) {
                if(域名ls(域名ame(), "password")) {
                    域名alue(域名ame(), "******");
                }
            }
        }
        
        return resultSet;
    }

}

plugin定义好以后,要想让插件起作用,需要把插件加入到MybatisSqlSessionFactoryBean中,代码如下(见标黄的部分)

package 域名域名ig;

import 域名Source;
import 域名域名essionFactory;
import 域名域名erScan;
import 域名域名域名wired;
import 域名.域名SourceProperties;
import 域名.域名SourceBuilder;
import 域名域名;
import 域名域名iguration;
import 域名.域名MatchingResourcePatternResolver;
import 域名域名pe;
import 域名域名域名tisPlusInterceptor;
import 域名域名域名nationInnerInterceptor;
import 域名域名域名tisSqlSessionFactoryBean;
import 域名域名域名torRuntimeException;
import 域名域名域名torExecutorInterceptor;
import 域名域名域名torParameterHandlerInterceptor;
import 域名域名域名torResultSetHandlerInterceptor;
import 域名域名域名torStatementHandlerInterceptor;
import 域名域名riDataSource;
import 域名域名j;

@Slf4j
@Configuration
@MapperScan("域名域名")
public class DataSourceConfig {
    
    @Autowired
    private DataSourceProperties properties;
    
    @Bean
    public DataSource dataSource() {
        域名("数据库连接池创建中......");
        HikariDataSource dataSource = null;
        try {
            dataSource = 域名te(域名lassLoader())
                    .type(域名s)
                    .driverClassName(域名rmineDriverClassName())
                    .url(域名rmineUrl())
                    .username(域名rmineUsername()).password(域名assword())
                    .build();
        } catch (Exception e) {
            throw new XfactorRuntimeException("get password failed!", e);
        }
        return dataSource;
    }
    
    @Bean
    public SqlSessionFactory xfactorSqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        域名ataSource(dataSource());
//        域名lugins(mybatisPlusInterceptor(), new AnalyseMybatisPluginsInterceptor());
        域名lugins(new XfactorResultSetHandlerInterceptor(), 
                new XfactorParameterHandlerInterceptor(), 
                new XfactorStatementHandlerInterceptor(),
                new XfactorExecutorInterceptor());
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        域名apperLocations(域名esources("classpath*:mapper/*xml"));
        域名ypeAliasesPackage("域名域名.entity");
        return 域名bject();
    }
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        域名nnerInterceptor(new PaginationInnerInterceptor(域名GRE_SQL));
        return interceptor;
    }
}

场景二:更改查询库表的schema(场景类似于修改sql语句),首先定义了一个XfactorStatementHandlerInterceptor,代码如下:

package 域名域名per;

import 域名ection;
import 域名域名域名ingStatementHandler;
import 域名域名域名ementHandler;
import 域名域名rceptor;
import 域名域名rcepts;
import 域名域名cation;
import 域名域名ature;
import 域名域名Object;
import 域名域名emMetaObject;
import 域名域名riProxyConnection;
import 域名域名j;

@Slf4j
@Intercepts({
    @Signature(type= 域名s, method = "prepare", args = {域名s, 域名s}),
})
public class XfactorStatementHandlerInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        域名("===StatementHandler===");
        ((HikariProxyConnection)域名rgs()[0]).setSchema("notes");//这里改schema
        
        //这里改sql,但是如果是对select的sql语句进行修改,建议实现域名s的plugin中进行,当前方式改select语句insert/update/delete都会走这个判断
        MetaObject metaObject = 域名bject(((RoutingStatementHandler)域名arget()).getBoundSql());
        String execSql = (String) 域名alue("sql");
        if(域名tsWith("select ") || 域名tsWith("SELECT ")) {
            域名alue("sql", 域名at(" order by id desc"));
        }
        return 域名eed();
    }

}

结合以上两个场景可知,有些目的可以通过多个类型的plugin都能实现,但是肯定有一个是最佳方案的(plugin定义好以后,要想让插件起作用,需要把插件加入到MybatisSqlSessionFactoryBean中,代码见加粗的部分)。

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。