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中,代码见加粗的部分)。

大白话讲解Mybatis的plugin(Interceptor)的使用的更多相关文章

  1. Mybatis那些事-拦截器(Plugin+Interceptor)

    作者:yhjyumi的专栏 数据权限实现(Mybatis拦截器+JSqlParser) Mybatis的拦截器实现机制,使用的是JDK的InvocationHandler. 当我们调用Paramete ...

  2. mybatis的plugin

    1.Mybatis-Plugin的设计思路 听起来一个挺神奇的单词,插件.说白了就是使用了Jdk自带的动态代理.在需要的时候进行代理.AOP怎么用,他就怎么用. Plugin类等价于Invocatio ...

  3. 互联网轻量级框架SSM-查缺补漏第八天(MyBatis插件plugin使用及原理)

    简言:今天进行第八天的记录(只是写了八天).有的时候看的多,有的时候看的少,看的少的时候就攒几天一起写了.而今天这个插件我昨天写了一下午,下班没写完就回去了,今天把尾收了,再加上一个过程图方便下面原理 ...

  4. Mybatis插件Plugin

    Mybatis开源Plugin中最熟知的pagehelper,重点made in China 很多人开始用pagehelper时候,肯定很纳闷,以mysql为例,明明没有加limit语句,为什么打印出 ...

  5. com.github.pagehelper.PageHelper cannot be cast to org.apache.ibatis.plugin.Interceptor

    在MyBatis的配置文件中修改对pageHelper的配置修改前 <plugins> <plugin interceptor="com.github.pagehelper ...

  6. Mybatis之plugin插件设计原理

    大多数框架,都支持插件,用户可通过编写插件来自行扩展功能,Mybatis也不例外. 我们从插件配置.插件编写.插件运行原理.插件注册与执行拦截的时机.初始化插件.分页插件的原理等六个方面展开阐述. 一 ...

  7. Magento 2 Plugin - Interceptor - Magento 2插件 - 拦截器-插件开发

    Magento 2 Plugin - Interceptor - Magento 2插件 - 拦截器 Magento 2 Plugin is a technical plugin for your b ...

  8. Mybatis辅助神器-MyBatis Log Plugin

    1. 问题描述 Java操作数据库的两台流行天王-mybatis和hibernate,mytabis和hibernate的区别不想废话了,以前用hibernate,最近几年一直用的mybatis,目前 ...

  9. IDEA优秀插件分享之---Mybatis Log Plugin

    小伙伴们在使用mybatis的时候有时候会出现一些sql异常,这个时候就需要对执行的sql语句进行检查.然而mybatis一般使用log4j打印执行的sql语句,类型下面这种的: 这个时候如果sql语 ...

随机推荐

  1. 从产业链、架构和技术三个层面,看元宇宙与RPA的发展关系

    你可能还不知道,元宇宙也将带动RPA高速发展 一文读懂RPA如何赋能元宇宙,虚拟空间更需要RPA无处不在 三个层面,解读元宇宙如何利好RPA行业发展 从产业链.架构和技术三个层面,看元宇宙与RPA的发 ...

  2. 国庆总结:echarts自定义颜色主题,保证你看的明明白白

    为什么需要使用颜色主题 随着用户审美越来越高,不再是过去那样只注重功能. 所以对界面的颜色样式都具有一定的审美要求 此时颜色是否好看就非常重要了 因为人都是视觉动物 对界面的第一印象肯定都是颜色. 如 ...

  3. centos6.5 oracle 卸载

    1.使用SQL*PLUS停止数据库 [oracle@OracleTest oracle]$ sqlplus / as sysdba SQL> shutdown immediate; SQL> ...

  4. react之四种组件中DOM样式设置方式

    1.行内样式 想给虚拟dom添加行内样式,需要使用表达式传入样式对象的方式来实现 行内样式需要写入一个样式对象,而这个样式对象的位置可以放在很多地方 例如:render函数里.组件原型上.外链js文件 ...

  5. RabbitMQ持久化机制、内存磁盘控制(四)

    一.持久化 如果看到这一篇文章的朋友,都是有经验的开发人员,对持久化的概念就不用再做过多的解析了,经过前面的几篇文章,其实不难发现RabbitMQ 的持久化其实就只分交换器持久化.队列持久化和消息持久 ...

  6. Apache ShardingSphere 邀您相约 Open Source Day

    Apache ShardingSphere 很荣幸接受 AnitaB.org 的邀请参加 OpenSourceDay Summer 2021 活动. Open Source Day (OSD) 是一个 ...

  7. 洛谷4072 SDOI2016征途 (斜率优化+dp)

    首先根据题目中给的要求,推一下方差的柿子. \[v\times m^2 = m\times \sum x^2 - 2 \times sum \times sum +sum*sum \] 所以\(ans ...

  8. 爬虫逆向基础,理解 JavaScript 模块化编程 webpack

    关注微信公众号:K哥爬虫,QQ交流群:808574309,持续分享爬虫进阶.JS/安卓逆向等技术干货! 简介 在分析一些站点的 JavaScript 代码时,比较简单的代码,函数通常都是一个一个的,例 ...

  9. 40个Python入门小程序

    有不少同学学完Python后仍然很难将其灵活运用.我整理 37 个Python入门的小程序.在实践中应用Python会有事半功倍的效果. 分享 Github 项目,里面收集了 Python 学习资料 ...

  10. Golang通脉之类型定义

    自定义类型 在Go语言中有一些基本的数据类型,如string.整型.浮点型.布尔等数据类型, Go语言中可以使用type关键字来定义自定义类型. type是Go语法里的重要而且常用的关键字,type绝 ...