当前位置:Gxlcms > 数据库问题 > mybatis源码解析之如何调用JDBC的预处理器Statement完成交互

mybatis源码解析之如何调用JDBC的预处理器Statement完成交互

时间:2021-07-01 10:21:17 帮助过:45人阅读

技术图片

 

 1.1预编译的三种执行器

技术图片

简单执行器(Statement)存在sql注入问题,发送一条一条静态sql语句(包含参数),传输体量比较大。

预处理执行器(PreparedStatement)可以防止sql注入问题,发送一条sql语句包含若干组参数,传输体量比较小。

存储过程处理器(CallableStatement)支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持。

1.2简单执行器与预处理执行器的区别?

1)简单执行器

  • 执行静态sql存在sql注入问题,不需要设置参数。
  • 相同的sql查询参数不同时会组装成不同参数的多条静态sql进行多次发送,从而多次编译多次执行。

2)预处理执行器

  • 执行动态sql可以防止sql注入问题,需要设置预编译参数。
  • 相同的sql查询参数不同时会组装成一条动态sql,多组不同参数进行发送,从而一次编译多次执行。

二、mybatis执行过程

技术图片

 

 SqlSession设计采用的是门面模式-----方便调用。

这里主要讲述一下mybatis执行器

2.1mybatis的Executor体系

技术图片

CachingExecutor类中是MyBatis关于二级缓存相关的逻辑。

BaseExecutor抽象类中是MyBatis关于一级缓存相关的逻辑。

SimpleExecutor、ReuseExecutor、BatchExecutor是继承自BaseExecutor具体的执行器类,具体执行数据库的增删改查。

1)SimpleExecutor简单执行器

简单执行每次都会创建一个新的预处理器(PrepareStatement),废话不多说上源码分析

这里先介绍一个类ConnectionLogger,该类是mybatis调用jdbc三种预处理器Statement进行交互的一个类(后面会介绍mybatis是如何调用jdbc框架的)。

主要代码如下:

技术图片

2)ReuseExecutor可重用执行器

相同的sql只进行一次预处理,执行结果多次使用,废话不多说上源码分析

由以下源码可知:ReuseExecutor通过属性statementMap 缓存Statement来实现可重用性。

  1. <span style="color: #008080;"> 1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> ReuseExecutor <span style="color: #0000ff;">extends</span><span style="color: #000000;"> BaseExecutor {
  2. </span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">final</span> Map<String, Statement> statementMap = <span style="color: #0000ff;">new</span><span style="color: #000000;"> HashMap();
  3. </span><span style="color: #008080;"> 3</span>
  4. <span style="color: #008080;"> 4</span>
  5. <span style="color: #008080;"> 5</span> <span style="color: #008000;">//</span><span style="color: #008000;">判断如果Statement存在了直接从缓存中获取,若不存在创建并添加到缓存中</span>
  6. <span style="color: #008080;"> 6</span> <span style="color: #0000ff;">private</span> Statement prepareStatement(StatementHandler handler, Log statementLog) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> SQLException {
  7. </span><span style="color: #008080;"> 7</span> BoundSql boundSql =<span style="color: #000000;"> handler.getBoundSql();
  8. </span><span style="color: #008080;"> 8</span> String sql =<span style="color: #000000;"> boundSql.getSql();
  9. </span><span style="color: #008080;"> 9</span> <span style="color: #000000;"> Statement stmt;
  10. </span><span style="color: #008080;">10</span> <span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span><span style="color: #000000;">.hasStatementFor(sql)) {
  11. </span><span style="color: #008080;">11</span> stmt = <span style="color: #0000ff;">this</span><span style="color: #000000;">.getStatement(sql);
  12. </span><span style="color: #008080;">12</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.applyTransactionTimeout(stmt);
  13. </span><span style="color: #008080;">13</span> } <span style="color: #0000ff;">else</span><span style="color: #000000;"> {
  14. </span><span style="color: #008080;">14</span> Connection connection = <span style="color: #0000ff;">this</span><span style="color: #000000;">.getConnection(statementLog);
  15. </span><span style="color: #008080;">15</span> stmt = handler.prepare(connection, <span style="color: #0000ff;">this</span><span style="color: #000000;">.transaction.getTimeout());
  16. </span><span style="color: #008080;">16</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.putStatement(sql, stmt);
  17. </span><span style="color: #008080;">17</span> <span style="color: #000000;"> }
  18. </span><span style="color: #008080;">18</span> <span style="color: #000000;"> handler.parameterize(stmt);
  19. </span><span style="color: #008080;">19</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> stmt;
  20. </span><span style="color: #008080;">20</span> <span style="color: #000000;"> }
  21. </span><span style="color: #008080;">21</span> <span style="color: #008000;">//</span><span style="color: #008000;">判断缓存是否存在且有效</span>
  22. <span style="color: #008080;">22</span> <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">boolean</span><span style="color: #000000;"> hasStatementFor(String sql) {
  23. </span><span style="color: #008080;">23</span> <span style="color: #0000ff;">try</span> {<span style="color: #008000;">//</span><span style="color: #008000;">缓存存在且缓存未失效(会话未关闭)</span>
  24. <span style="color: #008080;">24</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span>.statementMap.keySet().contains(sql) && !((Statement)<span style="color: #0000ff;">this</span><span style="color: #000000;">.statementMap.get(sql)).getConnection().isClosed();
  25. </span><span style="color: #008080;">25</span> } <span style="color: #0000ff;">catch</span><span style="color: #000000;"> (SQLException var3) {
  26. </span><span style="color: #008080;">26</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;
  27. </span><span style="color: #008080;">27</span> <span style="color: #000000;"> }
  28. </span><span style="color: #008080;">28</span> <span style="color: #000000;"> }
  29. </span><span style="color: #008080;">29</span>
  30. <span style="color: #008080;">30</span> <span style="color: #0000ff;">private</span><span style="color: #000000;"> Statement getStatement(String s) {
  31. </span><span style="color: #008080;">31</span> <span style="color: #0000ff;">return</span> (Statement)<span style="color: #0000ff;">this</span><span style="color: #000000;">.statementMap.get(s);
  32. </span><span style="color: #008080;">32</span> <span style="color: #000000;"> }
  33. </span><span style="color: #008080;">33</span>
  34. <span style="color: #008080;">34</span> <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> putStatement(String sql, Statement stmt) {
  35. </span><span style="color: #008080;">35</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.statementMap.put(sql, stmt);
  36. </span><span style="color: #008080;">36</span> <span style="color: #000000;"> }
  37. </span><span style="color: #008080;">37</span> }

3)BatchExecutor批处理执行器

BatchExecutor批处理执行器顾名思义支持批量处理,废话不多说上源码分析

由以下源码可知,BatchExecutor通过属性statementList存储Statement和属性batchResultList存储返回的结果集BatchResult(此时还没有真正填充结果集,只是new了一个对象),从而实现批量处理的功能。

技术图片

 

 三、mybatis究竟是如何调用jdbc的呢(那我们来探究一下)?

在此之前我们要先大致介绍一下StatementHandler体系

技术图片

 

 

 下面我们主要看一下RoutingStatementHandler类的源码

由以下源码可知该类是通过MappedStatement的属性statementType来判断mybatis执行时使用的哪个Handler

技术图片

假设MyBatis使用的SimpleStatementHandler执行器,MappedStatement的statementType属性值为PREPARED,我们以此为例进行讲解MyBatis具体调用JDBC预处理器Statement的流程

首先MyBatis通过MappedStatement配置的statementType属性值PREPARED,由RoutingStatementHandler路由类分配得到PreparedStatementHandler,而后执行PreparedStatementHandler的instantiateStatement()方法具体代码如下:

技术图片

 

 进一步走入ConnectionLogger代理类的invoke()方法,会根据上一步的方法名创建对应的PreparedStatement

技术图片

 

 此处ConnectionLogger类是一个代理类,其主要功能是对instantiateStatement()方法中调用的prepareStatement、prepareCall、createStatement方法进行判断分别创建jdbc对应的Statement的代理日志类,完成mybatis与jdbc的交互。

以上各个Statement的代理日志类其核心功能还是Statement对象,只是通过代理模式增强添加了日志打印;由此,可以借鉴系统 与系统或接口与接口之间的交互在某种程度上可以使用代码模式进行交互以方便增强添加日志打印监控功能。

mybatis源码解析之如何调用JDBC的预处理器Statement完成交互

标签:style   获取   注入   sql查询   门面模式   keyset   对象   tty   日志打印   

人气教程排行