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 ...
随机推荐
- OmniThreadLibrary学习笔记
http://blog.sina.com.cn/s/articlelist_1157240623_6_1.html 非常好的控件,仔细看
- ASP.NET判断是否为手机登录
protected void Page_Load(object sender, EventArgs e) { MobileHandle(); } 页面加载时候判断是否为手机登录 protected v ...
- UWP开发---抓包分析
一,使用工具 ①Fiddler 摘自百度百科Fiddler简介: Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fi ...
- 【转】PowerDesigner快捷键
一般快捷键 快捷键 说明 F4 打开检查模型窗口,检查模型 F5 如果图窗口内的图改变过大小,恢复为原有大小即正常大小 F6 放大图窗口内的图 F7 缩小图窗口内的图 F8 在图窗口内中查看全部图内容 ...
- Regular Expression
It's a very elegant summary of regular expression from The AWK Programming Language. 1. The regular ...
- Django(框架、模板)
day65 参考:https://www.cnblogs.com/liwenzhou/p/8296964.html Django框架的设计模式借鉴了MVC框架的思想,也是分成三部分,来降低各个部分之间 ...
- 全世界最顶级黑客同时沸腾在DEF CON 25,是怎样一种体验?
2017,我在这里!圆你黑客梦!! 被称为黑客“世界杯”与“奥斯卡”的美国黑帽技术大会Black Hat和世界黑客大会DEF CON 是众多黑客心中最神圣的梦! 有位小表弟告诉我说:Black ...
- python收集jvm数据
之前前辈用 java 写的收集 jvm 脚本, 不太方便组内小伙伴维护, 遂用 python 重写了 #!/usr/bin/env python # -*- coding: utf-8 -*- # F ...
- C#6.0语言规范(八) 语句
C#提供了各种语句.大多数这些语句对于使用C和C ++编程的开发人员来说都很熟悉. statement : labeled_statement | declaration_statement | em ...
- 深度学习环境配置:Ubuntu16.04下安装GTX1080Ti+CUDA9.0+cuDNN7.0完整安装教程(多链接多参考文章)
本来就对Linux不熟悉,经过几天惨痛的教训,参考了不知道多少篇文章,终于把环境装好了,每篇文章或多或少都有一些用,但没有一篇完整的能解决我安装过程碰到的问题,所以决定还是自己写一篇我安装过程的教程, ...