MybatisPlus 自定义插件实现拦截SQL修改功能(实例详解)

 更新时间:2023年11月15日 09:52:01   作者:爱码猿  
这篇文章主要介绍了MybatisPlus 自定义插件实现拦截SQL修改功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

最近项目内使用MybatisPlus整合Phoenix实现对HBase进行操作,但是Phoenix的sql语法和MySQL不太一样,导致得在列上加@TableField申明列簇名称和列名称,不太友好,所以自己写了个插件拦截sql并进行修改

package org.gjw.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author guojunwang
 * @date 2021-11-29 17:06
 */
public class PhoenixMPPlugin extends JsqlParserSupport implements InnerInterceptor {
    /**
     * 查询时处理逻辑
     */
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        //通过 JSqlParser工具修改查询sql后执行
        mpBs.sql(parserSingle(mpBs.sql(), null));
    }
    /**
     * 增删改时 处理逻辑
     */
    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        //增删改调用 JSqlParser工具修改sql后执行
        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            mpBs.sql(parserMulti(mpBs.sql(), null));
        }
    }
    /**
     * 以处理查询sql为例,增删改的实现可根据自己的业务实现
     */
    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        //此处处理select逻辑 将字符串拼接上 双引号
        SelectBody selectBody = select.getSelectBody();
        if(selectBody instanceof PlainSelect) reformatPlainSelect((PlainSelect) selectBody);
    }
    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        System.out.println( "新增前调用,可修改sql" );
    }
    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        System.out.println( "删除前调用,可修改sql" );
    }
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        System.out.println("修改调用,可修改sql");
    }
//---------------以下为处理sql操作,根据自己业务功能完善
    /**
     * 处理查询字段
     */
    private List<SelectItem> disposeSelectColumn(List<SelectItem> selectItems){
        return selectItems.stream().map( this::resetSelectItem ).collect(Collectors.toList());
    }
    private SelectItem resetSelectItem( SelectItem selectItem ){
        //如果不符合直接返回
        if( !(selectItem instanceof SelectExpressionItem) ) return selectItem;
        SelectExpressionItem item = (SelectExpressionItem)selectItem;
        //如果是列
        if( item.getExpression() instanceof Column ){
            Column columnExp = (Column)item.getExpression();
            return new SelectExpressionItem( reFormatSelectColumn( columnExp,item.getAlias() ) );
        }
        //如果是函数
        if( item.getExpression() instanceof Function){
            Function function = (Function) item.getExpression();
            return new SelectExpressionItem( reFormatFunction( function ) );
        }
         return item;
    }
    /**
     * 重新格式化 查询语句
     * @param plainSelect 查询语句
     * @return 格式化的查询语句
     */
    public void reformatPlainSelect(PlainSelect plainSelect){
        //处理要查询的字段
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        //处理查询条件
        plainSelect.setSelectItems( disposeSelectColumn( selectItems ) );
        //处理 where 条件
        plainSelect.setWhere( disposeSelectWhere( plainSelect.getWhere() )  );
    }
    /**
     * 重新格式化列
     * @param columnExp 列
     * @param alias 列别名
     * @return 格式化的列
     */
    private Column reFormatSelectColumn( Column columnExp,Alias alias ){
        if( columnExp == null ) return columnExp;
        //表名和列簇名会在一起
        String tableAndCFName= columnExp.getTable() == null ? "" : columnExp.getTable().toString();
        //字段名
        String columnName= columnExp.getColumnName();
        //根据 `.` 分隔方便处理表名和列簇名
        String[] tableAndCFInfo = tableAndCFName.split("\\.");
        // 可能会出现很多情况 列名  列簇.列名  表名.列簇.列名 表名.列名
        String tableName = tableAndCFInfo[0];
        String cf        = tableAndCFInfo[tableAndCFInfo.length - 1];
        //如果表名和字段名相等,只有3种情况: 列名  表名.列名  列簇.列名
        if( StrUtil.equals(tableName,cf)  && StrUtil.isNotBlank(tableName) ){
            //判断前缀是表名还是列名  要求列簇必须全大写 表名不能全大写
            //如果全大写这是列簇名
            if( StrUtil.equals(cf.toUpperCase(),cf) ) {
                tableName = "";
            }else cf = ""; //否则是表名
        }
        StringBuilder finalName = new StringBuilder();
        //如果表名不为空 拼接表名
        if( StrUtil.isNotBlank( tableName ) )   finalName.append( tableName ).append( "." );
        //如果列簇名不为空 拼接列簇名
        if( StrUtil.isNotBlank( cf ) ) finalName.append( appendPrefixAndSuffix(cf) ).append(".");
        //拼接字段名
        finalName.append( appendPrefixAndSuffix(columnName) );
        //拼接别名: as xxx
        if( alias !=null ) finalName.append(" ").append( alias.getName() );
        //重新格式化列名 封装返回
        return new Column( finalName.toString() );
    }
    /**
     * 重新格式化查询函数
     * @param function 函数
     * @return 格式化的函数
     */
    private Function reFormatFunction( Function function ){
        List<Expression> expressions = function.getParameters().getExpressions();
        //对于是列的参数进行格式化
        expressions = expressions.stream().map(exp -> {
            if (exp instanceof Column) return reFormatSelectColumn((Column) exp, null);
            return exp;
        }).collect(Collectors.toList());
        //重新设置回去
        function.getParameters().setExpressions(expressions);
        return function;
    }
    /**
     * 重新格式化子查询
     * @param subSelect 子查询
     * @return 格式化的函数
     */
    private SubSelect reFormatSubSelect( SubSelect subSelect ){
        if( subSelect.getSelectBody() instanceof PlainSelect ){
            reformatPlainSelect( (PlainSelect)subSelect.getSelectBody() );
        }
        return subSelect;
    }
    public Expression disposeSelectWhere(Expression expression){
        if( !(expression instanceof BinaryExpression) ) return expression;
        BinaryExpression binaryExpression =(BinaryExpression)expression;
        //如果左边还是多条件的
        if( binaryExpression.getLeftExpression() instanceof BinaryExpression){
            disposeSelectWhere( binaryExpression.getLeftExpression() );
        }
        //如果右边还是多条件的
        if( binaryExpression.getRightExpression() instanceof BinaryExpression){
            disposeSelectWhere( binaryExpression.getRightExpression() );
        }
        //如果左边表达式是列信息 格式化
        if(  binaryExpression.getLeftExpression() instanceof Column ){
            Column newColumn = reFormatSelectColumn((Column) binaryExpression.getLeftExpression(), null);
            binaryExpression.setLeftExpression( newColumn );
        }
        //如果左边表达式是 子查询 processPlainSelect
        if(binaryExpression.getLeftExpression() instanceof SubSelect){
            SubSelect subSelect = (SubSelect)binaryExpression.getLeftExpression();
            if( subSelect.getSelectBody() instanceof PlainSelect ){
                reformatPlainSelect( (PlainSelect)subSelect.getSelectBody() );
            }
        }
        //如果右边是列信息 格式化
        if(  binaryExpression.getRightExpression() instanceof Column ){
            Column newColumn = reFormatSelectColumn((Column) binaryExpression.getLeftExpression(), null);
            binaryExpression.setRightExpression( newColumn );
        }
        //如果右边表达式是 子查询 processPlainSelect
        if( binaryExpression.getRightExpression() instanceof SubSelect){
            SubSelect subSelect = (SubSelect)binaryExpression.getRightExpression();
            reFormatSubSelect( subSelect );
        }
        return binaryExpression;
    }
    private String appendPrefixAndSuffix(String str){
        final String PREFIX = "\"";
        final String SUFFIX = "\"";
        //如果已经有前缀了直接返回
        if( str.contains(PREFIX) ) return str;
        //拼接前缀和后缀
        return new StringBuilder().append(PREFIX).append(str).append(SUFFIX).toString();
    }
}

使用: 编写配置类配置MybatisPlus并设置插件

@MapperScan(value = "org.gjw.mapper.phoenix",sqlSessionTemplateRef = "phoenixSqlSessionTemplate",sqlSessionFactoryRef = "phoenixSqlSessionFactory")
@Configuration
public class PhoenixConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.phoenix")
    public DataSource phoenixDataSource(){
        return new HikariDataSource();
    }
    @Bean
    public SqlSessionFactory phoenixSqlSessionFactory( @Qualifier("phoenixDataSource") @Autowired DataSource phoenixDataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource( phoenixDataSource() );
        sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:/phoenixMapper/**/*.xml"));
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor( new PhoenixMPPlugin() );
        sqlSessionFactoryBean.setPlugins( interceptor );
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisConfiguration.setMapUnderscoreToCamelCase(true);
        mybatisConfiguration.setLogImpl(StdOutImpl.class);
        sqlSessionFactoryBean.setConfiguration(mybatisConfiguration);
        return sqlSessionFactoryBean.getObject();
    }
    @Bean
    public SqlSessionTemplate phoenixSqlSessionTemplate( @Qualifier("phoenixSqlSessionFactory") @Autowired SqlSessionFactory phoenixSqlSessionFactory){
        return new SqlSessionTemplate( phoenixSqlSessionFactory );
    }
    @Bean
    public DataSourceTransactionManager phoenixDataSourceTransactionManager(@Qualifier("phoenixDataSource") @Autowired DataSource phoenixDataSource){
        return new DataSourceTransactionManager(phoenixDataSource);
    }
}

到此这篇关于MybatisPlus 自定义插件实现拦截SQL修改功能的文章就介绍到这了,更多相关MybatisPlus 拦截SQL内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java Quartz触发器CronTriggerBean配置用法详解

    Java Quartz触发器CronTriggerBean配置用法详解

    这篇文章主要介绍了Java Quartz触发器CronTriggerBean配置用法详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • SpringBoot使用jasypt加解密密码的实现方法

    SpringBoot使用jasypt加解密密码的实现方法

    这篇文章主要介绍了SpringBoot使用jasypt加解密密码的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • SpringBoot访问windows共享文件的方法

    SpringBoot访问windows共享文件的方法

    这篇文章主要介绍了SpringBoot访问windows共享文件,项目使用minio存储且不在同一台服务器上,为了优化速度决定使用windows共享功能进行文件传输,本文结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-02-02
  • Java业务中台确保数据一致性的解决方案

    Java业务中台确保数据一致性的解决方案

    数据一致性通常指关联数据之间的逻辑关系是否正确和完整。而数据存储的一致性模型则可以认为是存储系统和数据使用者之间的一种约定。如果使用者遵循这种约定,则可以得到系统所承诺的访问结果
    2021-10-10
  • 详解Java异常处理中throw与throws关键字的用法区别

    详解Java异常处理中throw与throws关键字的用法区别

    这篇文章主要介绍了详解Java异常处理中throw与throws关键字的用法区别,这也是Java面试题目中的常客,需要的朋友可以参考下
    2015-11-11
  • 一文读懂Java Iterator(迭代器)

    一文读懂Java Iterator(迭代器)

    这篇文章主要介绍了Java Iterator(迭代器)的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • Netty学习之理解selector原理示例

    Netty学习之理解selector原理示例

    这篇文章主要为大家介绍了Netty学习之理解selector原理示例使用分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪<BR>
    2023-07-07
  • Spring MVC实现mysql数据库增删改查完整实例

    Spring MVC实现mysql数据库增删改查完整实例

    这篇文章主要介绍了Spring MVC实现mysql数据库增删改查完整实例,从创建一个web项目开始,分享了项目结构以及具体Java代码和前端页面等相关内容,具有一定借鉴价值,需要的朋友可以了解下。
    2017-12-12
  • Java ArrayList的基本概念和作用及动态数组的机制与性能

    Java ArrayList的基本概念和作用及动态数组的机制与性能

    在Java中,ArrayList是一个实现了List接口的动态数组,它可以根据需要自动增加大小,因此可以存储任意数量的元素,这篇文章主要介绍了探秘Java ArrayList的基本概念和作用及动态数组的机制与性能,需要的朋友可以参考下
    2023-12-12
  • Spring boot集成Kafka消息中间件代码实例

    Spring boot集成Kafka消息中间件代码实例

    这篇文章主要介绍了Spring boot集成Kafka消息中间件代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05

最新评论