MyBatis原理第四篇——statementHandler对象(sqlSession内部核心实现,插件的基础)
首先约定文中将的四大对象是指:executor, statementHandler,parameterHandler,resultHandler对象。(为了方便下面的文章说道四大对象就专指它们)
讲到statementHandler,毫无疑问它是我们四大对象最重要的一个,它的任务就是和数据库对话。在它这里会使用parameterHandler和ResultHandler对象为我们绑定SQL参数和组装最后的结果返回。
一、statementHandler对象的定义:
首先我们先来看看statementHandler接口的定义:
- public interface StatementHandler {
- Statement prepare(Connection connection)
- throws SQLException;
- void parameterize(Statement statement)
- throws SQLException;
- void batch(Statement statement)
- throws SQLException;
- int update(Statement statement)
- throws SQLException;
- <E> List<E> query(Statement statement, ResultHandler resultHandler)
- throws SQLException;
- BoundSql getBoundSql();
- ParameterHandler getParameterHandler();
- }
这里有几个重要的方法,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方法的实现,
- @Override
- public Statement prepare(Connection connection) throws SQLException {
- ErrorContext.instance().sql(boundSql.getSql());
- Statement statement = null;
- try {
- statement = instantiateStatement(connection);
- setStatementTimeout(statement);
- setFetchSize(statement);
- return statement;
- } catch (SQLException e) {
- closeStatement(statement);
- throw e;
- } catch (Exception e) {
- closeStatement(statement);
- throw new ExecutorException("Error preparing statement. Cause: " + e, e);
- }
- }
- protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
显然我们通过源码更加关注抽象方法instantiateStatement是做了什么事情。它依旧是一个抽象方法,那么它就有其实现类。那就是之前说的那几个具体的StatementHandler对象,让我们看看PreparedStatementHandler:
- @Override
- protected Statement instantiateStatement(Connection connection) throws SQLException {
- String sql = boundSql.getSql();
- if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
- String[] keyColumnNames = mappedStatement.getKeyColumns();
- if (keyColumnNames == null) {
- return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
- } else {
- return connection.prepareStatement(sql, keyColumnNames);
- }
- } else if (mappedStatement.getResultSetType() != null) {
- return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
- } else {
- return connection.prepareStatement(sql);
- }
- }
好这个方法非常简单,我们可以看到它主要是根据上下文来预编译SQL,这是我们还没有设置参数。设置参数的任务是交由,statement接口的parameterize方法来实现的。
3、parameterize方法:
上面我们在prepare方法里面预编译了SQL。那么我们这个时候希望设置参数。在Statement中我们是使用parameterize方法进行设置参数的。
让我们看看PreparedStatementHandler中的parameterize方法:
- @Override
- public void parameterize(Statement statement) throws SQLException {
- parameterHandler.setParameters((PreparedStatement) statement);
- }
很显然这里很简单是通过parameterHandler来实现的,我们这篇文章只是停留在statementhandler的程度,等我们讲解parameterHandler的时候再来看它如何实现吧,期待一下吧。
4、query/update方法
我们用了prepare方法预编译了SQL,用了parameterize方法设置参数,那么我们接下来肯定是想执行SQL,而SQL无非是两种:
一种是进行查询——query,另外就是更新——update。
这些方法都很简单,让我们看看PreparedStatementHandler的实现:
- @Override
- public int update(Statement statement) throws SQLException {
- PreparedStatement ps = (PreparedStatement) statement;
- ps.execute();
- int rows = ps.getUpdateCount();
- Object parameterObject = boundSql.getParameterObject();
- KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
- keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
- return rows;
- }
- ......
- @Override
- public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
- PreparedStatement ps = (PreparedStatement) statement;
- ps.execute();
- return resultSetHandler.<E> handleResultSets(ps);
- }
我们可以看到如果是进行update的,它将会执行生成主键的操作(插入数据要自动生成主键的时候),然后就返回影响行数。
如果是进行query的就更加简单了,它就是执行SQL语句,然后讲结果使用resultHandler的handleResultSets去完成我们的结果组装。至于resultHandler的内部实现还是很复杂的,值得期待哦。这里我们暂且不讲等待下一章吧。
5、总结
StatementHandler是MyBatis四大对象里面最重要的对象,它的方法是十分重要的,也是我们插件的基础。
当我们需要改变sql的时候,显然我们要在预编译SQL(prepare方法前加入修改的逻辑)。
当我们需要修改参数的时候我们可以在调用parameterize方法前修改逻辑。或者使用ParameterHandler来改造设置参数。
我们需要控制组装结果集的时候,也可以在query方法前后加入逻辑,或者使用ResultHandler来改造组装结果。
懂的这些方法,才能理解我需要拦截什么对象,如何处理插件,这是MyBatis的核心内容。
MyBatis原理第四篇——statementHandler对象(sqlSession内部核心实现,插件的基础)的更多相关文章
- DWR第四篇之对象传参
1. 本示例在第一篇架构基础上添加代码 2. 首先,在dwr.xml文件里添加对象转换器 3. 编写Person实体类 package com.skyer.vo; import java.util.A ...
- SqlSession 内部运行
<深入浅出MyBatis技术原理与实战>p150页 SqlSession内部运行图 四大对象在流程中的操作. 1.准备sql.StatementHandler 的prepare方法进行sq ...
- Mybatis源码分析之Cache一级缓存原理(四)
之前的文章我已经基本讲解到了SqlSessionFactory.SqlSession.Excutor以及Mpper执行SQL过程,下面我来了解下myabtis的缓存, 它的缓存分为一级缓存和二级缓存, ...
- 如约而至,Java 10 正式发布! Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十四)Redis缓存正确的使用姿势 努力的孩子运气不会太差,跌宕的人生定当更加精彩 优先队列详解(转载)
如约而至,Java 10 正式发布! 3 月 20 日,Oracle 宣布 Java 10 正式发布. 官方已提供下载:http://www.oracle.com/technetwork/java ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例
日常啰嗦 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(三)代码测试>讲了不为和不能两个状态,针对不为,只能自己调整心态了,而对于不能,本文会结合一 ...
- Mybatis第四篇【多表连接】
Mybatis多表连接 我们在学习Hibernate的时候,如果表涉及到两张的话,那么我们是在映射文件中使用<set>..<many-to-one>等标签将其的映射属性关联起来 ...
- 《深入理解mybatis原理》 MyBatis的架构设计以及实例分析
作者博客:http://blog.csdn.net/u010349169/article/category/2309433 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简 ...
- MyBatis 原理浅析——基本原理
前言 MyBatis 是一个被广泛应用的持久化框架.一个简单的使用示例如下所示,先创建会话工厂,然后从会话工厂中打开会话,通过 class 类型和配置生成 Mapper 接口的代理实现,最后使用 Ma ...
- 《深入理解mybatis原理》 Mybatis初始化机制具体解释
对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...
随机推荐
- map 小模板~~~ 写的不好 继续添加
#include<map>#include<string.h>#include<iostream>using namespace std; int main(){ ...
- 分形之花篮(Flower Basket)
这一篇展示的图形与上一篇文章分形之皇冠(Crown)很相似. 核心代码: static void FractalFlowerBasket(const Vector3& vStart, cons ...
- python安装mysql-python依赖包
# 背景 新公司,对换工作了!接口自动化使用的是python的behave框架,因此需要折腾python了,而公司配的笔记本是windows的,因此要在windows下折腾python了 # 步骤 项 ...
- Windows核心编程:第7章 线程调度、优先级和关联性
Github https://github.com/gongluck/Windows-Core-Program.git //第7章 线程调度.优先级和关联性.cpp: 定义应用程序的入口点. // # ...
- kotlin面向对象-笔记
- 【五校联考3day2】C
題意: 現有一平面直角坐標系,有n個點,每一個點必須向某一個方向發射射線,且任意一條射線必須與某一條坐標軸平行.定義一種發射射線的方案是合法的,則方案必須滿足: 1.沒有一條射線交叉 2.沒有一條射線 ...
- [vuejs] 终端npm run dev 不能自动打开浏览器运行项目解决办法
终端执行: npm run dev 出现: I Your application is running here: http://localhost:8080 但并没有打开浏览器运行项目 解决办法: ...
- C#6.0语言规范(十八) 不安全代码
前面章节中定义的核心C#语言与C和C ++的区别在于它省略了作为数据类型的指针.相反,C#提供了引用和创建由垃圾收集器管理的对象的能力.这种设计与其他功能相结合,使C#成为比C或C ++更安全的语言. ...
- 为什么要先 git add 才能 git commit
1. git 的 add ,是一个容易引起疑问的命令.在 subversion 中的 svn add 动作是将某个文件加入版本控制,而 git add的意义完全不同. 同时, git diff --c ...
- WebRTC开发基础(WebRTC入门系列2:RTCPeerConnection)
RTCPeerConnection的作用是在浏览器之间建立数据的“点对点”(peer to peer)通信. 使用WebRTC的编解码器和协议做了大量的工作,方便了开发者,使实时通信成为可能,甚至在不 ...