ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

定制Mybatis拦截器开发【含源码追溯】

2022-01-14 16:00:09  阅读:180  来源: 互联网

标签:拦截器 查询 源码 sql Mybatis StatementHandler 拦截 执行


 1、需求分析

需求:在代码层面获得Mybatis执行的SQL,修改SQL,并执行修改后的SQL

方案:Mybatis 拦截器:

 

 

 注意:添加拦截器后,会拦截所有的方法

 思考:其实拦截器就等同于Spring的AOP编程

 细粒度:Mybatis框架中,sql最后都会交给Sqlsession执行,拦截器拦截的其实就是:

  • 1、Executor执行阶段
  • 2、ParameterHandler参数处理阶段
  • 3、StatementHandler预编译处理阶段
  • 4、ResultSetHandler结果集处理阶段 
注:我们在执行Sql之前,需要先获取Mybatis的SqlSession对象,但框架的SqlSession下面还有四大对象,
所以SqlSession只是个甩手掌柜,
真正干活的却是Executor等四大对象:Executor,StatementHandler,ParameterHandler,ResultSetHandler。

比较形象的画图比喻:

 

 

 继续分析:

理论上来说,如果想拦截,这四个对象的所有方法均可拦截到

但是原则是原则,实际是实际

就像Java中的String类,它里面这么多方法,总有几个常用的

在这边主要拦截两个常用对象:

 

 按照Mybatis执行Sql的周期来说,Executor会把sql交给StatementHandler处理,

 所以我们常见的拦截方法还是StatementHandler,而且这个阶段的Sql已完成预编译,占位符出现,对Sql来说比较全面了(此时除了参数未赋值,其他都全了,就等执行了)

2、熟悉拦截器的开发结构

 1、编码 

  类实现Intercepyor接口@Override

// 核心拦截逻辑编写    
public Object intercept(Invocation invocation) throws Throwable { // 编写拦截逻辑
System.out.println("编写拦截逻辑");
// 放行该方法 return invocation.proceed(); } // 把这个拦截器的目标传给下一个拦截器 @Override public Object plugin(Object target) { // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数 if (target instanceof StatementHandler) { System.out.println("Wrapper::"); return Plugin.wrap(target, this); } else { System.out.println("pass on Wrapper::"); return target; } @Override public void setProperties(Properties properties) { //此处可以接收到配置文件的property参数,就像工作流里面的局部变量,如果业务上需要定义参数,可以通过此方法 // System.out.println(properties.getProperty("name")); }

 

  类 标注拦截的方法和参数(防止方法重载,需参数判别方法的唯一性)

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class AccessControlInterceptor implements Interceptor {

}

 

2、配置(Configure配置)

  在  mybatis-config.xml  中通过plugins 标签配置拦截器

  或者利用SpringBoot快速定义@Configure标签标识Bean

思考:如何控制拦截器的执行顺序?
         比如我想让拦截器x执行完之后,再执行拦截器y
参考链接:https://juejin.cn/post/6926849748481605646

好的,到这里。执行到每一个mapper文件的接口对应的sql 都会起作用了。(包括PageHelper 自动生成的Count查询,同样会被拦截)

思考:1、如何只拦截SELECT类型的sql,自动过滤掉update类型?
     2、如何拦截指定的mapper接口方法?
         
方案:1、可以通过拦截方法的invocation参数利用反射获取本次执行sql的 
        MappedStatement,进而获得sqlCommandType
         
     2、可在mapper接口方法层面添加自定义注解,同样是通过反射的方式 
        获得注解标志位判定应不应该进行拦截,还可通过注解传递有含义的 
        value

 

3、更细致的分析 

为什么要拦截StatementHandler的prepare呢

1、Executor 会交给StatementHandler,

    在StatementHandler接口实现类的方法调用中,

    StatementHandler的prepare生产出的Statemen会作为参数提供给CRUD和批处理之类的操作

    拦截prepare方法是一劳永逸

调用结构顺序如下图:

 

2、拦截prepare方法可以获得当前的Connection对象,

     Connection对象是所有Java接入所有数据库的规则接口,可操作性的东西会很多

     sql片段、事务,等同于JDBC  为所欲为 

 

 

 4、拦截器应用举例

一般什么情况下会使用拦截器:当需要操作sql时

例如:分页(添加limit sql片段),乐观锁(设置版本号校验,校验成功直接更新版本和数据,校验失败直接提示线程争抢失败请重试)

应用开发之一、pagehelper如何使用拦截器实现分页

 

找一个

   1、com.github.pagehelper.PageInterceptor 拦截请求

 

 

 第 71 - 第99行都是校验参数、创建缓存之类的操作

   2、凭空捏造Count查询

第 100 行,pagehelper官方团队给出的注释是:查询总数

 

 

 继续追代码,进入count(..)

 

先判断是否存在手写的 count 查询,

存在Count查询,用原来的 ms ,更新部分参数,直接执行并返回结果,

没有的话,根据当前的 ms 创建一个返回值为 Long 类型的 ms,并放到缓存中去

最后执行Count查询,并返回Count

 

接着做了一个小优化

处理查询总数,当查询总数为 0 时,直接结束查询

 

   3、修改原始查询sql 拼接 limit进行分页

根据上一步查询来的Count,结合当前页,计算limit值,并交给Executor去执行query

 

至此,pagehelper整个分页粗略过程完成

4、思考使用的细节

     PageHelper是如何做到只对紧跟着的第一条SQL起作用的?即使在下面添加再多的select,他仍然只对第一个select情有独钟

 

 

 

先揭晓答案: PageHelper.startPage在当前线程中创建一个线程变量 t1,

                   需要的时候就去当前线程获取 t1,使用完一次就在finally中remove掉 t1

                   受限于代码同步执行的特性,实现了只对第一个select起作用

                 (思考:若开启新线程,在主查询之前执行异步查询,Pagehelper特性会失效吗?)

源代码追溯:

 (未完待续。。。)

 应用开发之二、 乐观锁使用拦截器

 (未完待续。。。)

 

 

标签:拦截器,查询,源码,sql,Mybatis,StatementHandler,拦截,执行
来源: https://www.cnblogs.com/dk1024/p/15802245.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有