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. Jenkins打包安卓时提示没同意constraintLayout的license的解决方法

    使用Jenkins打包安卓项目时,报错并失败,错误信息: You have not accepted the license agreements of the following SDK compo ...

  2. vc让界面保持最上层

    vc让界面保持最上层.事实上就一个函数就ok了, ::SetWindowPos(AfxGetMainWnd()->m_hWnd,HWND_TOPMOST,-1,-1,-1,-1,0);

  3. mysql实时增量备份

    采用binlog日志的好处 掌控所有更改操作,必要时可用于恢复数据 数据库主从复制的必要条件 [root@localhost~]# vim /etc/my.cnf [mysqld] .. .. log ...

  4. 008-docker-安装-tomcat:8.5.38-jre8

    1.搜索镜像 docker search tomcat 2.拉取合适镜像 查询tags:https://hub.docker.com/ docker pull tomcat:8.5.38-jre8 d ...

  5. 帝国cms建站-动态获取栏目id

    <?php $fcr=explode('|',$class_r[$GLOBALS[navclassid]][featherclass]); $topbclassid=$fcr[1]?$fcr[1 ...

  6. python的__getitem__

    #这个方法返回与指定键相关的值.对序列来说,键应该是0~n-1的整数,其中n为序列的长度.对映射来说,键可以是任何类型. class Test(object): def __init__(self): ...

  7. Linux ssh将命令放入后台

    如何在关闭ssh连接的情况下,让程序继续运行? 对Unix,Linux类服务器维护经常是通过ssh完成的,而有些操作比较费时,如更新程序等.此时如果断开ssh连接的话,更新程序就会随之被中断.如何保证 ...

  8. CentOS安装pycharm

    1.官网下载包 2.Linux下解压 3.进入bin目录 4.赋权:chmod +x pycharm.sh 5.安装:./pycharm.sh 6.添加链接:ln -s /home/pycharm/p ...

  9. JS 8-3 prototype属性

    动态修改Student.prototype的属性时,会影响已创建或将要创建的实例. 如果将整个Student.prototype赋值为新的对象,对已经创建的实例是不会修改的,会影响后续创建的实例. n ...

  10. [LeetCode] 709. To Lower Case_Easy

    Implement function ToLowerCase() that has a string parameter str, and returns the same string in low ...