首先约定文中将的四大对象是指:executor, statementHandler,parameterHandler,resultHandler对象。(为了方便下面的文章说道四大对象就专指它们)

讲到statementHandler,毫无疑问它是我们四大对象最重要的一个,它的任务就是和数据库对话。在它这里会使用parameterHandler和ResultHandler对象为我们绑定SQL参数和组装最后的结果返回。

一、statementHandler对象的定义:

首先我们先来看看statementHandler接口的定义:

  1. public interface StatementHandler {
  2. Statement prepare(Connection connection)
  3. throws SQLException;
  4. void parameterize(Statement statement)
  5. throws SQLException;
  6. void batch(Statement statement)
  7. throws SQLException;
  8. int update(Statement statement)
  9. throws SQLException;
  10. <E> List<E> query(Statement statement, ResultHandler resultHandler)
  11. throws SQLException;
  12. BoundSql getBoundSql();
  13. ParameterHandler getParameterHandler();
  14. }

这里有几个重要的方法,prepare,parameterize和query,update,他们的作用是不一样的。

在MyBatis实现了statementHandler的有四个类:

RoutingStatementHandler,这是一个封装类,它不提供具体的实现,只是根据Executor的类型,创建不同的类型StatementHandler。

SimpleStatementHandler,这个类对应于JDBC的Statement对象,用于没有预编译参数的SQL的运行。

PreparedStatementHandler 这个用于预编译参数SQL的运行。

CallableStatementHandler 它将实存储过程的调度。

在MyBatis中,Configuration对象会采用new RoutingStatementHandler()来生成StatementHandler对象,换句话说我们真正使用的是RoutingStatementHandler对象,然后它会根据Executor的类型去创建对应具体的statementHandler对象(SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler)。

然后利用具体statementHandler的方法完成所需要的功能。那么这个具体的statementHandler是保存在RoutingStatementHandler对象的delegate属性的,所以当我们拦截statementHandler的时候就要常常访问它了。它们的关系如下图所示。

二、prepare方法

首先prepare方法是用来编译SQL的,让我们看看它的源码实现。这里我们看到了BaseStatementHandler对prepare方法的实现,

  1. @Override
  2. public Statement prepare(Connection connection) throws SQLException {
  3. ErrorContext.instance().sql(boundSql.getSql());
  4. Statement statement = null;
  5. try {
  6. statement = instantiateStatement(connection);
  7. setStatementTimeout(statement);
  8. setFetchSize(statement);
  9. return statement;
  10. } catch (SQLException e) {
  11. closeStatement(statement);
  12. throw e;
  13. } catch (Exception e) {
  14. closeStatement(statement);
  15. throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
  16. }
  17. }
  18. protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

显然我们通过源码更加关注抽象方法instantiateStatement是做了什么事情。它依旧是一个抽象方法,那么它就有其实现类。那就是之前说的那几个具体的StatementHandler对象,让我们看看PreparedStatementHandler:

  1. @Override
  2. protected Statement instantiateStatement(Connection connection) throws SQLException {
  3. String sql = boundSql.getSql();
  4. if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
  5. String[] keyColumnNames = mappedStatement.getKeyColumns();
  6. if (keyColumnNames == null) {
  7. return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
  8. } else {
  9. return connection.prepareStatement(sql, keyColumnNames);
  10. }
  11. } else if (mappedStatement.getResultSetType() != null) {
  12. return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  13. } else {
  14. return connection.prepareStatement(sql);
  15. }
  16. }

好这个方法非常简单,我们可以看到它主要是根据上下文来预编译SQL,这是我们还没有设置参数。设置参数的任务是交由,statement接口的parameterize方法来实现的。

3、parameterize方法:

上面我们在prepare方法里面预编译了SQL。那么我们这个时候希望设置参数。在Statement中我们是使用parameterize方法进行设置参数的。

让我们看看PreparedStatementHandler中的parameterize方法:

  1. @Override
  2. public void parameterize(Statement statement) throws SQLException {
  3. parameterHandler.setParameters((PreparedStatement) statement);
  4. }

很显然这里很简单是通过parameterHandler来实现的,我们这篇文章只是停留在statementhandler的程度,等我们讲解parameterHandler的时候再来看它如何实现吧,期待一下吧。

4、query/update方法

我们用了prepare方法预编译了SQL,用了parameterize方法设置参数,那么我们接下来肯定是想执行SQL,而SQL无非是两种:

一种是进行查询——query,另外就是更新——update。

这些方法都很简单,让我们看看PreparedStatementHandler的实现:

  1. @Override
  2. public int update(Statement statement) throws SQLException {
  3. PreparedStatement ps = (PreparedStatement) statement;
  4. ps.execute();
  5. int rows = ps.getUpdateCount();
  6. Object parameterObject = boundSql.getParameterObject();
  7. KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
  8. keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
  9. return rows;
  10. }
  11. ......
  12. @Override
  13. public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  14. PreparedStatement ps = (PreparedStatement) statement;
  15. ps.execute();
  16. return resultSetHandler.<E> handleResultSets(ps);
  17. }

我们可以看到如果是进行update的,它将会执行生成主键的操作(插入数据要自动生成主键的时候),然后就返回影响行数。

如果是进行query的就更加简单了,它就是执行SQL语句,然后讲结果使用resultHandler的handleResultSets去完成我们的结果组装。至于resultHandler的内部实现还是很复杂的,值得期待哦。这里我们暂且不讲等待下一章吧。

5、总结

StatementHandler是MyBatis四大对象里面最重要的对象,它的方法是十分重要的,也是我们插件的基础。

 

当我们需要改变sql的时候,显然我们要在预编译SQL(prepare方法前加入修改的逻辑)。

当我们需要修改参数的时候我们可以在调用parameterize方法前修改逻辑。或者使用ParameterHandler来改造设置参数。

我们需要控制组装结果集的时候,也可以在query方法前后加入逻辑,或者使用ResultHandler来改造组装结果。

懂的这些方法,才能理解我需要拦截什么对象,如何处理插件,这是MyBatis的核心内容。

 

MyBatis原理第四篇——statementHandler对象(sqlSession内部核心实现,插件的基础)的更多相关文章

  1. DWR第四篇之对象传参

    1. 本示例在第一篇架构基础上添加代码 2. 首先,在dwr.xml文件里添加对象转换器 3. 编写Person实体类 package com.skyer.vo; import java.util.A ...

  2. SqlSession 内部运行

    <深入浅出MyBatis技术原理与实战>p150页 SqlSession内部运行图 四大对象在流程中的操作. 1.准备sql.StatementHandler 的prepare方法进行sq ...

  3. Mybatis源码分析之Cache一级缓存原理(四)

    之前的文章我已经基本讲解到了SqlSessionFactory.SqlSession.Excutor以及Mpper执行SQL过程,下面我来了解下myabtis的缓存, 它的缓存分为一级缓存和二级缓存, ...

  4. 如约而至,Java 10 正式发布! Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十四)Redis缓存正确的使用姿势 努力的孩子运气不会太差,跌宕的人生定当更加精彩 优先队列详解(转载)

    如约而至,Java 10 正式发布!   3 月 20 日,Oracle 宣布 Java 10 正式发布. 官方已提供下载:http://www.oracle.com/technetwork/java ...

  5. Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例

    日常啰嗦 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(三)代码测试>讲了不为和不能两个状态,针对不为,只能自己调整心态了,而对于不能,本文会结合一 ...

  6. Mybatis第四篇【多表连接】

    Mybatis多表连接 我们在学习Hibernate的时候,如果表涉及到两张的话,那么我们是在映射文件中使用<set>..<many-to-one>等标签将其的映射属性关联起来 ...

  7. 《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

    作者博客:http://blog.csdn.net/u010349169/article/category/2309433 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简 ...

  8. MyBatis 原理浅析——基本原理

    前言 MyBatis 是一个被广泛应用的持久化框架.一个简单的使用示例如下所示,先创建会话工厂,然后从会话工厂中打开会话,通过 class 类型和配置生成 Mapper 接口的代理实现,最后使用 Ma ...

  9. 《深入理解mybatis原理》 Mybatis初始化机制具体解释

    对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...

随机推荐

  1. 关于DFS和BFS的理解 以及坐标的定义

    http://blog.csdn.net/bool_isprime/article/details/5803018DFS: 1: 坐标类型搜索 :这种类型的搜索题目通常来说简单的比较简单,复杂的通常在 ...

  2. pycharm的基本使用

    1.设置注释字体的颜色,如下图 2.常用快捷键 Ctrl + Space 基本的代码完成(类.方法.属性) Ctrl + Alt + Space 类名完成 Ctrl + Shift + Enter 语 ...

  3. selenium在操作隐藏元素时会报错,怎么判断元素是隐藏的?

    首先页面元素隐藏有五种方法: 1. opacity: 0; opacity 属性的意思是设置一个元素的透明度.它不是为改变元素的边界框(bounding box)而设计的.这意味着将 opacity ...

  4. CentOS7中配置vsftpd

    1.yum -y install vsftpd  安装vsftpd 2.配置vsftpd的配置文件(/etc/vsftpd/vsftpd.conf)主要修改以下配置内容 #不允许匿名访问 anonym ...

  5. TDateTimePicker中Date与Time的误导

    Delphi DateTime,Date,Time TDateTimePicker DateTimeDateTimeDateTimePicker 一.DateTime,Date,Time存储方式本质上 ...

  6. [模板]最小割树(Gomory-Hu Tree)(luogu4897)

    给定一个\(n\)个点\(m\)条边的无向连通图,多次询问两点之间的最小割 两点间的最小割是这样定义的:原图的每条边有一个割断它的代价,你需要用最小的代价使得这两个点不连通 Input 第一行两个数\ ...

  7. 游戏AI玩伴,是“神队友”还是“猪队友”?

    “一代英豪”暴雪迎来了自己的暴风雪. 2月13日,动视暴雪公布了2018年全年财报.财报显示,暴雪第四季度营业收入仅为28.4亿美元,低于华尔街分析师预期的30.4亿美元.在公布了财报业绩后,该公司又 ...

  8. TmsHttpClientUtil

    package com.sprucetec.tms.utils; import java.io.IOException;import java.security.GeneralSecurityExce ...

  9. ASP.NET Core WebApi 项目部署到 IIS 服务器的总结

    Point: - ASP.NET Core WebApi 项目 - 发布到 IIS 服务器 1. 选择 File System 2. 输入要发布到的路径 # 其它默认,直接发布 3. 打开 IIS,添 ...

  10. iOS--各种bug详解

    1.为什么传的参数都对,但是就是请求不下来数据. 答:检查下传的字符串中,是不是有多的空格. 例如: 错误:{"startIndex":"1","en ...