文章目录

  • 拦截器接口介绍
  • SQL拦截改写
    • 定义拦截器接口 Interceptor
      • 添加拦截器
  • 关于我

拦截器接口介绍

Mybatis 允许在以映射SQL语句执行过程中的某一点进行拦截调用。默认情况下,Mybatis允许使用插件来拦截的接口和方法有以下几个

  • Executor(update、query、flushStatements、commit、rollback、getTransaction、clos、isClosed)
  • ParameterHandler(getParameterObject、setParameters)
  • ResultSetHandler(handlerResultSets、handleCursorResultSets、handleOutPutParameters)
  • StatementHandler(prepare、parameterize、batch、update、query)

要实现我们自己的拦截器我们就需要实现拦截器接口Interceptor,其中Interceptor接口中的方法如下

public interface Interceptor {/*** 执行的拦截方法,Invocation 中可以拿到很多我们需要的属性,比如拦截方法名(getMethod()),方法参数(getArgs()),SQL等*/Object intercept(Invocation var1) throws Throwable;/*** var1代表要拦截的对象,这里实现自己要拦截对象的逻辑*/Object plugin(Object var1);/*** 插件属性传递*/void setProperties(Properties var1);
}

如果配置多个拦截器Mybatis会按照顺序依次执行

实现拦截器接口的同时我们还需要对实现类加注解@Intercepts进行配置

注解中的Signature主要包含的属性如下

  • type:设置拦截的接口,可选参数为前面的四个接口
  • method:设置拦截接口中的方法名,可选值是前面四个接口的方法
  • args:设置拦截方法的参数类型,通过方法名和参数类型可唯一确定一个方法

其中最为常用的配置是如下

@Intercepts(@Signature(type = Executor.class, method = "update",args = {MappedStatement.class, Object.class}))

该签名会拦截所有的INSERT、UPDATE、DELETE

SQL拦截改写

接下来我们就来实现我们的SQL拦截改写,要改写的SQL很简单,对所有的Select 表名后面加Final

定义拦截器接口 Interceptor

这里要说明的是实现方式由两种,一种是直接使用 replaceAll替换表名 在加final关键字,这种出错几率很小,所有SQL都支持,第二种使用SQL解析器解析SQL然后再拼接SQL,这种实现方式对不同的数据库需要不同的实现类,可以定义一个SQL改写接口,对于SQL改写可以自己选择不同的开源框架,比如sqlparser、Apache Calcite、druid等,这里我使用的druid解析的SQL,只是做了简单的SQL解析,没有测试大量的SQL,所以一下demo仅供参考

@Component
@Intercepts(@Signature(type = Executor.class, method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class MybatisInterceptor implements Interceptor {static int MAPPED_STATEMENT_INDEX = 0;// 这是对应上面的args的序号@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 获取拦截方法的参数Object[] args = invocation.getArgs();MappedStatement mappedStatement = (MappedStatement) args[0];// 拦截方法参数MapperMethod.ParamMap method = (MapperMethod.ParamMap)args[1];Integer customerId = (Integer)method.get(("customerId"));if (!filterObject(customerId)) {//只处理拦截SQLreturn invocation.proceed();}// 通过StatementHandler 获取执行SQLBoundSql boundSql = mappedStatement.getBoundSql(args[1]);String sql = boundSql.getSql();
//        System.out.println("获取到的SQL: " + sql);String newSql = updateSql(sql);BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), newSql,boundSql.getParameterMappings(), boundSql.getParameterObject());// 把新的查询放到statement里MappedStatement newMs = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql));for (ParameterMapping mapping : boundSql.getParameterMappings()) {String prop = mapping.getProperty();if (boundSql.hasAdditionalParameter(prop)) {newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));}}final Object[] queryArgs = invocation.getArgs();queryArgs[MAPPED_STATEMENT_INDEX] = newMs;return invocation.proceed();}private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());builder.resource(ms.getResource());builder.fetchSize(ms.getFetchSize());builder.statementType(ms.getStatementType());builder.keyGenerator(ms.getKeyGenerator());if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {builder.keyProperty(ms.getKeyProperties()[0]);}builder.timeout(ms.getTimeout());builder.parameterMap(ms.getParameterMap());builder.resultMaps(ms.getResultMaps());builder.resultSetType(ms.getResultSetType());builder.cache(ms.getCache());builder.flushCacheRequired(ms.isFlushCacheRequired());builder.useCache(ms.isUseCache());return builder.build();}public static class BoundSqlSqlSource implements SqlSource {private final BoundSql boundSql;public BoundSqlSqlSource(BoundSql boundSql) {this.boundSql = boundSql;}public BoundSql getBoundSql(Object parameterObject) {return boundSql;}}/*** SQL改写* @param sql* @return java.lang.String* @author wh* @date 2021/1/21*/private String updateSql(String sql) {//        String convertedSql = sql.replaceAll("t_order", "order_detail final");StringBuilder newSql = new StringBuilder("select ");PGSQLStatementParser mySqlStatementParser = new PGSQLStatementParser(sql);SQLSelectStatement sqlSelectStatement = mySqlStatementParser.parseSelect();SQLSelect sqlSelect = sqlSelectStatement.getSelect();SQLSelectQuery sqlSelectQuery = sqlSelect.getQuery();if (sqlSelectQuery instanceof PGSelectQueryBlock) {PGSelectQueryBlock pgSelectQueryBlock = (PGSelectQueryBlock) sqlSelectQuery;List<SQLSelectItem> coulums = pgSelectQueryBlock.getSelectList();for (int i = 0; i < coulums.size(); i++) {if (i == coulums.size() - 1) {newSql.append(coulums.get(i));} else {newSql.append(coulums.get(i)).append(",");}}new PGOutputVisitor(new StringBuilder());PGOutputVisitor tableName = new PGOutputVisitor(new StringBuilder());pgSelectQueryBlock.getFrom().accept(tableName);
//            System.out.println(tableName.getAppender());newSql.append(" from ").append(tableName.getAppender()).append(" final");PGOutputVisitor where = new PGOutputVisitor(new StringBuilder());// 获取where 条件pgSelectQueryBlock.getWhere().accept(where);newSql.append(" where ").append(where.getAppender());PGOutputVisitor groupBy = new PGOutputVisitor(new StringBuilder());SQLSelectGroupByClause sqlGroup = pgSelectQueryBlock.getGroupBy();if (Objects.nonNull(sqlGroup)) {sqlGroup.accept(groupBy);newSql.append(" ").append(groupBy.getAppender());}}return newSql.toString();}/*** 要拦截的对象* @param target* @return java.lang.Object* @author wh* @date 2021/1/21*/@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}/*** 插件属性传递* @param properties* @return void* @author wh* @date 2021/1/21*/@Overridepublic void setProperties(Properties properties) {}/*** 默认拦截所有SQL* @param* @return boolean* @author wh* @date 2021/1/21*/public boolean filterObject(Integer customerId) {return true;}}

添加拦截器

@Beanpublic SqlSessionFactory clickHouseSqlSessionFactoryBean() throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();factory.setDataSource(clickhouseDataSource());factory.setTypeAliasesPackage(MODEL_PACKAGE);//添加拦截器factory.setPlugins(new Interceptor[]{mybatisInterceptor});//添加XML目录ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();factory.setMapperLocations(resolver.getResources("classpath:mapper/clickhouse/*.xml"));//开启驼峰命名转换factory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);return factory.getObject();}

关于我

觉得文章不错请扫码关注我吧

Mybatis插件开发(拦截SQL并改写SQL)相关推荐

  1. 一文教你如何使用Mybatis Plugin 以及Druid Filer 改写SQL

    背景 工作中偶尔会碰到需要统一修改SQL的情况,例如有以下表结构: CREATE TABLE test_user ( id int(11) NOT NULL AUTO_INCREMENT, accou ...

  2. Mybatis 通过拦截器动态修改SQL

    01 使用场景 当我们在多租户的项目中,编写SQL语句都要带上tenant字段,用于区分不同的租户只能操作自己的数据. 比如,像下面的SQL select * from member where id ...

  3. Mybatis Plugin 以及Druid Filer 改写SQL

    背景 工作中偶尔会碰到需要统一修改SQL的情况,例如有以下表结构: CREATE TABLE `test_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`ac ...

  4. mybatis使用拦截器显示sql,使用druid配置连接信息

    mybatis使用拦截器显示sql,使用druid配置连接信息 mybatis sql Druid 1.显示出sql内容: 新建2个类: MybatisInterceptor :拦截sql,并获得输出 ...

  5. 老年人教程:MyBatis拦截器动态修改SQL(更新与插入)语句

    注:本文编写与 2019年12月17日, 内容可能存在时效性问题. 数据库使用MySQL5.7 集成于SpringBoot 2.0.X , 引用国产的开源工具类Hutool 本教程建议显示大纲视图 配 ...

  6. Mybatis Plus 是如何实现动态 SQL 语句的?原理你懂吗?

    作者 | 稻草江南 来源 | https://juejin.cn/post/6883081187103866894 Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,那么它是怎么 ...

  7. Mybatis源码阅读(一):Mybatis初始化1.3 —— 解析sql片段和sql节点

    *************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如 ...

  8. mybatis以及预编译如何防止SQL注入

    SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者).[摘自] SQL injection - Wikipedia SQL ...

  9. MyBatis是持久化层框架(SQL映射框架)-操作数据库

    MyBatis是持久化层框架(SQL映射框架)-操作数据库 1.环境搭建 1).创建一个java工程: 2).创建测试库,测试表,以及封装数据的javaBean,和操作数据库的dao接口 创建表:自己 ...

最新文章

  1. Java高并发编程基础之AQS
  2. 为什么只有很少的人听说过西工大这个名字?
  3. JavaWeb 项目安全问题及其解决方案
  4. 闲鱼发布:十大无用商品、创业雷区、塌房明星,内容劲爆与扎心...
  5. How to Use Command to Execute Git Push with Review from Sean
  6. 用不好PPT模板的本质原因是什么,怎么办
  7. JAVA通过SSL证书创建MS AD账户及设置密码
  8. 【程序设计】变量和常量
  9. [W3C][CSS]选择器(Selectors Level 3)[未完成]
  10. GC原理---对象可达判断
  11. MySQL索引(如何设计索引)
  12. 用汇编的眼光看C++(之指针2)
  13. 数字图像处理原理与实践:基于Visual C++开发
  14. QT软件开发: 获取CPU序列号、硬盘序列号、主板序列号 (采用wmic命令)
  15. html5的header标签
  16. wireshark过滤规则
  17. 650 storm 铃木v_V双暴风雨 2018款铃木V-Strom 650/650XT
  18. 华为系列服务器账号密码,常用设备管理口默认用户名密码汇总
  19. WordPress快速增加百度收录,加快网站内容抓取
  20. java web简单的网上名片管理系统

热门文章

  1. 内蒙古农业大学计算机学院,内蒙古农业大学计算机与信息工程学院介绍
  2. week8 B - 猫猫向前冲(拓扑排序)
  3. 计算机管理损坏的图像,损坏的图像,教您提示损坏的图像该怎么解决
  4. numpy.ndarray.transpose
  5. 微信分享位置原理的思考
  6. 02.unary 方法
  7. C语言运算符优先级表
  8. PyDev+Eclipse4.2.2如何正确显示中文
  9. 上诉法院力挺FCC“网络中立”规则
  10. 圆通开放平台电子面单下单接口,适用于第三方系统对接