StatementHandler解析

接口的作用是statement处理器,位于mybatis包的org.apache.ibatis.executor.statement目录下,源码如下:

 package org.apache.ibatis.executor.statement;

 import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List; import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.session.ResultHandler; public interface StatementHandler { //sql预编译,构建Statement对象
Statement prepare(Connection connection)
throws SQLException; //对prepare方法构建的预编译的SQL进行参数的设置
void parameterize(Statement statement)
throws SQLException; //批量处理
void batch(Statement statement)
throws SQLException; //执行预编译后的SQL--update语句
int update(Statement statement)
throws SQLException; //执行预编译后的SQL--select语句
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException; //获取SQL封装类BoundSql对象
BoundSql getBoundSql(); //获取参数处理器对象
ParameterHandler getParameterHandler(); }

可见StatementHandler的作用就是先通过prepare方法构建一个Statement对象,然后再调用其他方法对Statement对象进行处理

StatementHandler和Executor类似,StatementHandler也有两个实现类,BaseStatementHandler和RoutingStatementHandler

而BaseStatement又有三个子类实现它的抽象方法,下面再挨个分析,先看最简单的BaseStatementHandler

BaseStatementHandler解析

BaseStatementHandler是一个抽象父类,有三个子类继承于它,源码如下:

 package org.apache.ibatis.executor.statement;

 import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement; import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry; public abstract class BaseStatementHandler implements StatementHandler { protected final Configuration configuration;//全局配置
protected final ObjectFactory objectFactory;//对象工厂
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;//结果集处理器
protected final ParameterHandler parameterHandler;//参数处理器 protected final Executor executor;//执行器
protected final MappedStatement mappedStatement;//mapper的SQL对象
protected final RowBounds rowBounds;//分页参数 protected BoundSql boundSql;//sql封装对象 //构造方法
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory(); if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
} this.boundSql = boundSql; this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
} //返回boundSql
public BoundSql getBoundSql() {
return boundSql;
} //返回parameterHandler
public ParameterHandler getParameterHandler() {
return parameterHandler;
} //预编译SQL语句
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);//调用抽象方法构建statement对象是,但是没有具体实现,而是交给其子类去实现
setStatementTimeout(statement);//设置statement超时时间
setFetchSize(statement);//设置statement的fetchSize
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; //给statement对象设置timeout
protected void setStatementTimeout(Statement stmt) throws SQLException {
Integer timeout = mappedStatement.getTimeout();
Integer defaultTimeout = configuration.getDefaultStatementTimeout();
if (timeout != null) {
stmt.setQueryTimeout(timeout);
} else if (defaultTimeout != null) {
stmt.setQueryTimeout(defaultTimeout);
}
} //给statement对象设置fetchSize
protected void setFetchSize(Statement stmt) throws SQLException {
Integer fetchSize = mappedStatement.getFetchSize();
if (fetchSize != null) {
stmt.setFetchSize(fetchSize);
}
} //关闭statement
protected void closeStatement(Statement statement) {
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
//ignore
}
} //根据参数对象生成key
protected void generateKeys(Object parameter) {
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
ErrorContext.instance().store();
keyGenerator.processBefore(executor, mappedStatement, null, parameter);
ErrorContext.instance().recall();
} }

可以看出BaseStatementHandler只是实现了StatementHandler的三个方法,其中getBoundSql和getParameterHandler方法只是返回了由构造方法初始化的boundSql和parameterHandler属性,

而prepare方法是用于构建Statement对象的,但是BaseStatementHandler只是调用了自身的抽象方法instantiateStatement来创建,然后对statement对象进行其他处理,但是创建的过程则没有实现,而是交给了其子类去实现。

BaseStatementHandler有三个子类,分别为:
SimpleStatememtHandler:最简单的StatementHandler,处理不带参数运行的SQL
PreparedStatementHandler:预处理Statement的handler,处理带参数允许的SQL
CallableStatementHandler:存储过程的Statement的handler,处理存储过程SQL

先来看最简单的SimpleStatementHandler,它继承于BaseStatementHandler,所以它需要实现StatementHandler的接口,还需要重写父类BaseStatementHandler的抽象方法,源码如下:

 package org.apache.ibatis.executor.statement;

 import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List; import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds; public class SimpleStatementHandler extends BaseStatementHandler { //构造方法执行父类的构造方法
public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
} //执行update操作
public int update(Statement statement)
throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
//最终都是执行statement的getUpdateCount方法
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
} //给statement添加批量处理sql语句
public void batch(Statement statement)
throws SQLException {
String sql = boundSql.getSql();
statement.addBatch(sql);
} //执行查询语句
public <E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);//statement.execute方法执行sql语句
return resultSetHandler.<E>handleResultSets(statement);
} //构造Statement对象
protected Statement instantiateStatement(Connection connection) throws SQLException {
//通过Connection来create一个Statement对象
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
} //由于SimpleStatementHandler是处理没有参数的SQL,所以参数设置的方法无需任何处理
public void parameterize(Statement statement) throws SQLException {
// N/A
} }

可以看出主要是通过Connection创建一个Statement对象,然后通过调用Statement的execute方法执行sql语句

再看下PreparedStatementHandler,源码如下

 package org.apache.ibatis.executor.statement;

 import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List; import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds; public class PreparedStatementHandler extends BaseStatementHandler { //执行父类构造方法
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
} //执行update操作
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;
} //给preparedStatement对象添加批量处理sql语句
public void batch(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.addBatch();
} //执行preparedStatement的查询语句
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
} //构建Statement的子类PreparedStatement对象
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);
}
} //给Statement对象设置参数
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
} }

再看下RoutingStatementHandler,这个相当于一个StatementHandler的路由器,本身不实现任何功能,只是根据传入的参数来选择调用哪个类型的StatementHandler来处理,代码如下:

 public class RoutingStatementHandler implements StatementHandler {

 private final StatementHandler delegate;

 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

 switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
} } @Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
} @Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
} @Override
public void batch(Statement statement) throws SQLException {
delegate.batch(statement);
} @Override
public int update(Statement statement) throws SQLException {
return delegate.update(statement);
} @Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
} @Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
return delegate.queryCursor(statement);
} @Override
public BoundSql getBoundSql() {
return delegate.getBoundSql();
} @Override
public ParameterHandler getParameterHandler() {
return delegate.getParameterHandler();
}
}

可见RoutingStatementHandler的作用就是在构造的时候根据Statement类型来创建不同的处理器,然后调用对应的处理器来进行操作。而在StatementHandler在初始化的时候,都是通过RoutingStatementHandler来进行路由的,Configuration中的创建StatementHandler代码如下:

 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

再看看SimpleStatementHandler是如何执行sql语句的:

 @Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}

先通过Collection创建Statement对象

 @Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleResultSets(statement);
}

然后通过Statement对象执行sql语句,最后通过ResultSetHandler对象来处理执行后的结果。

mybatis源码解析10---StatementHandler解析的更多相关文章

  1. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  2. Spring mybatis源码篇章-MybatisDAO文件解析(二)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...

  3. Spring mybatis源码篇章-MybatisDAO文件解析(一)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...

  4. MyBatis源码部分简单地解析

    . 一.解析xml: > org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream, java.l ...

  5. Mybatis 源码之Plugin类解析

    public class Plugin implements InvocationHandler { private Object target; //目标对象 private Interceptor ...

  6. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  7. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  8. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  9. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  10. Spring mybatis源码学习指引目录

    前言: 分析了很多方面的mybatis的源码以及与spring结合的源码,但是难免出现错综的现象,为了使源码陶冶更为有序化.清晰化,特作此随笔归纳下分析过的内容.博主也为mybatis官方提供过pul ...

随机推荐

  1. LeetCode-111.Mininum Depth of Binary Tree

    Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shor ...

  2. aws小结

    IAM:亚马逊访问权限控制(AWS Identity and Access Management ) https://www.cnblogs.com/andy9468/p/10635019.html ...

  3. 我想要得那块牌—记烟台大学第一届"ACM讲堂"

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/sr19930829/article/details/26812621           2014年 ...

  4. what's the python之字符编码与文件处理

    用文本编辑器打开一个文件就是把一个文件读入了内存中 ,所以打开文件的操作也是在内存中的,断电即消失,所以若要保存其内容就必须点击保存让其存入硬盘中 python解释器执行py文件的原理 : 第一阶段: ...

  5. MySQL 5.7 Replication 相关新功能说明 (转)

    背景: MySQL5.7在主从复制上面相对之前版本多了一些新特性,包括多源复制.基于组提交的并行复制.在线修改Replication Filter.GTID增强.半同步复制增强等.因为都是和复制相关, ...

  6. 【剑指offer】二叉搜索树与双向链表

    一.题目: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 二.思路: 对于一棵搜索二叉树来说,中序遍历得到的即是有序的结果,所以整 ...

  7. eslint 代码规范2

    eslint 规则修改 需要修改规则:文件[.eslintrc.js] 在句末是不能有分号的,若想加分号, 报错: 添加代码: 'semi': ['error', 'always'] 不要使用制表符. ...

  8. ssm框架整合

    1.1 整合的思路 1.1.1 Dao层 使用mybatis框架.创建SqlMapConfig.xml.(可以是任意名字) 创建一个applicationContext-dao.xml   (通过sp ...

  9. [vue]模拟移动端三级路由: router-link位置体现router的灵活性

    小结 router-link可以随便放 router-view显示的是父组件的直接子组件的内容 想研究下移动三级路由的逻辑, 即 router-link和router-view 点首页--点新闻资讯( ...

  10. 用python 替换文件中的git地址

    有个需求要替换文件中git地址,要替换成的git地址是一个变量 本来想用sed替换但是git地址中有斜杠符号 需要转义,提前知道还好弄,如果是变量就不好处理了 #!/usr/bin/python3 # ...