您的当前位置:首页正文

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

2024-08-15 来源:易榕旅网
⼤⽩话讲解Mybatis的plugin(Interceptor)的使⽤

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码云https://gitee.com/liudahai/notes,项⽬架构是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\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!\ }

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\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\ }

return invocation.proceed(); }}

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

因篇幅问题不能全部显示,请点此查看更多更全内容