资讯专栏INFORMATION COLUMN

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

laznrbfe / 4102人阅读

摘要:提供了一个入口,可以让你在语句执行过程中的某一点进行拦截调用。

        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 com.lhclab.xfactor.dal.wrapper;import java.lang.reflect.Field;import java.sql.Statement;import java.util.List;import org.apache.commons.codec.binary.StringUtils;import org.apache.ibatis.executor.resultset.ResultSetHandler;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.plugin.Intercepts;import org.apache.ibatis.plugin.Invocation;import org.apache.ibatis.plugin.Signature;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.reflection.SystemMetaObject;import lombok.extern.slf4j.Slf4j;@Slf4j@Intercepts({    @Signature(type= ResultSetHandler.class,method = "handleResultSets",args = {Statement.class})    })public class XfactorResultSetHandlerInterceptor implements Interceptor {    @Override    public Object intercept(Invocation invocation) throws Throwable {        log.info("===ResultSetHandler===");        Object resultSet = invocation.proceed();        List resultList = (List)resultSet;        for(Object item : resultList) {            Class sourceClass = item.getClass();            MetaObject metaObject = SystemMetaObject.forObject(item);            Field[] fields = sourceClass.getDeclaredFields();            for(Field field : fields) {                if(StringUtils.equals(field.getName(), "password")) {                    metaObject.setValue(field.getName(), "******");                }            }        }                return resultSet;    }}

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

package com.lhclab.xfactor.dal.config;import javax.sql.DataSource;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;import org.springframework.boot.jdbc.DataSourceBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;import com.lhclab.xfactor.common.exception.XfactorRuntimeException;import com.lhclab.xfactor.dal.wrapper.XfactorExecutorInterceptor;import com.lhclab.xfactor.dal.wrapper.XfactorParameterHandlerInterceptor;import com.lhclab.xfactor.dal.wrapper.XfactorResultSetHandlerInterceptor;import com.lhclab.xfactor.dal.wrapper.XfactorStatementHandlerInterceptor;import com.zaxxer.hikari.HikariDataSource;import lombok.extern.slf4j.Slf4j;@Slf4j@Configuration@MapperScan("com.lhclab.xfactor.dal.dao")public class DataSourceConfig {        @Autowired    private DataSourceProperties properties;        @Bean    public DataSource dataSource() {        log.info("数据库连接池创建中......");        HikariDataSource dataSource = null;        try {            dataSource = DataSourceBuilder.create(properties.getClassLoader())                    .type(HikariDataSource.class)                    .driverClassName(properties.determineDriverClassName())                    .url(properties.determineUrl())                    .username(properties.determineUsername()).password(properties.getPassword())                    .build();        } catch (Exception e) {            throw new XfactorRuntimeException("get password failed!", e);        }        return dataSource;    }        @Bean    public SqlSessionFactory xfactorSqlSessionFactory() throws Exception {        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();        sqlSessionFactoryBean.setDataSource(dataSource());//        sqlSessionFactoryBean.setPlugins(mybatisPlusInterceptor(), new AnalyseMybatisPluginsInterceptor());        sqlSessionFactoryBean.setPlugins(new XfactorResultSetHandlerInterceptor(),                 new XfactorParameterHandlerInterceptor(),                 new XfactorStatementHandlerInterceptor(),                new XfactorExecutorInterceptor());        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();        sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/*xml"));        sqlSessionFactoryBean.setTypeAliasesPackage("com.lhclab.xfactor.dal.dao.entity");        return sqlSessionFactoryBean.getObject();    }        @Bean    public MybatisPlusInterceptor mybatisPlusInterceptor() {        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));        return interceptor;    }}

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

package com.lhclab.xfactor.dal.wrapper;import java.sql.Connection;import org.apache.ibatis.executor.statement.RoutingStatementHandler;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.plugin.Intercepts;import org.apache.ibatis.plugin.Invocation;import org.apache.ibatis.plugin.Signature;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.reflection.SystemMetaObject;import com.zaxxer.hikari.pool.HikariProxyConnection;import lombok.extern.slf4j.Slf4j;@Slf4j@Intercepts({    @Signature(type= StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),})public class XfactorStatementHandlerInterceptor implements Interceptor {    @Override    public Object intercept(Invocation invocation) throws Throwable {        log.info("===StatementHandler===");        ((HikariProxyConnection)invocation.getArgs()[0]).setSchema("notes");//这里改schema                //这里改sql,但是如果是对select的sql语句进行修改,建议实现Executor.class的plugin中进行,当前方式改select语句insert/update/delete都会走这个判断        MetaObject metaObject = SystemMetaObject.forObject(((RoutingStatementHandler)invocation.getTarget()).getBoundSql());        String execSql = (String) metaObject.getValue("sql");        if(execSql.startsWith("select ") || execSql.startsWith("SELECT ")) {            metaObject.setValue("sql", execSql.concat(" order by id desc"));        }        return invocation.proceed();    }}

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/123645.html

相关文章

  • 【深入浅出MyBatis笔记】插件

    摘要:插件插件接口在中使用插件,我们必须实现接口。它将直接覆盖你所拦截对象原有的方法,因此它是插件的核心方法。插件在对象中的保存插件的代理和反射设计插件用的是责任链模式,的责任链是由去定义的。 插件 1、插件接口 在MyBatis中使用插件,我们必须实现接口Interceptor。 public interface Interceptor { // 它将直接覆盖你所拦截对象原有的方法,因...

    leon 评论0 收藏0
  • Mybatis Interceptor 拦截器

    摘要:拦截器的使用场景主要是更新数据库的通用字段,分库分表,加解密等的处理。拦截器均需要实现该接口。拦截器拦截器的使用需要查看每一个所提供的方法参数。对应构造器,为,为,为。可参考拦截器原理探究。 拦截器(Interceptor)在 Mybatis 中被当做插件(plugin)对待,官方文档提供了 Executor(拦截执行器的方法),ParameterHandler(拦截参数的处理),Re...

    nemo 评论0 收藏0
  • 关于Mybatis拦截器对结果集拦截

    摘要:刚学习拦截器方面,在网上找了很多关于拦截器方面的文章,自己也尝试过写过几个,但是关于结果集的拦截始终没有找到合适的不要喷我,毕竟是新手。 刚学习Mybatis拦截器方面,在网上找了很多关于Mybatis拦截器方面的文章,自己也尝试过写过几个,但是关于结果集的拦截始终没有找到合适的(PS: 不要喷我,毕竟是新手)。也在segmentfault 上提问过,依然没有找到一个易于理解的,后来自...

    kohoh_ 评论0 收藏0
  • Spring-Mybatis运行机制概括

    摘要:使用这个类库中的类将会加载必要的工厂类和类。最终它并不会依赖于或来构建应用程序代码。下面对各部分作用总结下。和无缝整合的机制和的认识在讲如何无缝整合进之前,我们先认识下和这两个接口的作用。附上上篇博文地址原理概括。 前言 本篇是继上篇MyBatis原理概括延伸的,所以如果有小伙伴还没看上篇博文的话,可以先去看下,也不会浪费大家太多的时间,因为本篇会结合到上篇叙述的相关内容。 好,切入正...

    qieangel2013 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<