上一篇讲了SqlSession对象中的Executor,接下来将对SqlSession的另一个对象StatementHandler进行讲解。

一、StatementHandler介绍

StatementHandler是Mybatis中最重要的一个对象,它负责操作Statement与数据库进行交流,在此过程中还会调用ParameterHandler进行参数配置,使用ResultHandler将查询结果与实体类对象进行绑定。因而ParameterHandler与ResultHandler的创建是与StatementHandler相关联的。

StatementHandler是一个顶级接口,它的类图如下所示:

StatementHandler接口下有两个直接实现类BaseStatementHandler和RoutingStatementHandler

1、RoutingStatementHandler类

RoutingStatementHandler:是一个具体实现类,在这个类中并没有对Statement对象进行具体使用,只是根据得到Executor类型决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象。

可以简单理解为:

StatementHandler statmentHandler = new RountingStatementHandler();

2、BaseStatementHandler抽象类

BaseStatementHandler:是StatementHandler接口的另一个实现类,本身是一个抽象类,用于简化StatementHandler接口实现的难度,属于适配器设计模式体现。它有三个实现类:SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler。

在RountingStatementHandler创建时,就根据接收的传递的SQL语句来创建这个三个类型的对象。

SimpleStatementHandler:管理Statement对象向数据库中推送不需要预编译的SQL语句。

PreparedStatementHandler:管理PreparedStatementHandler对象向数据库推送预编译的SQL语句。

CallableStatementHandler:管理CallableStatement对象调用数据库中构造函数,即有存储过程的SQL语句。

二、StatementHandler接口

StatementHandler是一个接口,其代码如下:

public interface StatementHandler {

  Statement prepare(Connection connection, Integer transactionTimeout)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;

  <E> Cursor<E> queryCursor(Statement statement)throws SQLException;

  BoundSql getBoundSql();

  ParameterHandler getParameterHandler();

}

我们主要关注StatementHandler中的四个重要方法:prepare()、parameterize()、update()、query()

1、prepare方法

prepare方法主要是在BaseStatementHandler类中实现的,其代码如下:

  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
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);
}
}

prepare方法用于创建一个(Statement or PreparedStatement or CallableStatement)对象,并设置Statement对象的最大工作时间和一次性读取的最大数据量,然后将生成的Statement对象返回。

prepare方法只在BaseStatementHandler被实现,在其三个子类中没有被重写。它主要用于三个子类调用获得对应的Statement接口对象,依靠instantiateStatement(connection)方法来返回具体Statement接口对象。instantiateStatement方法是BaseStatementHandler中定义的抽象方法,由三个子类来具体实现,这里采用了模板方法模式。

SimpleStatementHandler的instantiateStatement方法

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();
}
}

2、instantiateStatement方法

PreparedStatementHandler的instantiateStatement方法

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);
}
}

CallableStatementHandler的instantiateStatement方法

protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getResultSetType() != null) {
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareCall(sql);
}
}

3、parameterize方法

parameterize方法用于传递参数,因此只在PreparedStatementHandler和CallableStatementHandler中被重写。

PreparedStatementHandler的parameterize方法

public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}

CallableStatementHandler的parameterize方法

public void parameterize(Statement statement) throws SQLException {
registerOutputParameters((CallableStatement) statement);
parameterHandler.setParameters((CallableStatement) statement);
}

他们都调用了parameterHandler进行参数赋值。

4、query方法

query方法主要用于输送查询查询语句,并将查询结果转换对应的实体类对象。

SimpleStatementHandler的query方法:

public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleCursorResultSets(statement);
}

PreparedStatementHandler的query方法:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}

CallableStatementHandler的query方法:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
List<E> resultList = resultSetHandler.<E>handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}

由代码可知:在得到查询结果后,都是使用ResultSetHandler对结果进行转换的。

5、update方法

update方法用于输送insert、update、delete语句并返回处理数据行。

SimpleStatementHandler的update方法:

public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
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;
}

PreparedStatementHandler的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;
}

CallableStatementHandler的update方法:

public int update(Statement statement) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
int rows = cs.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
resultSetHandler.handleOutputParameters(cs);
return rows;
}

二、StatementHandler对象创建

StatementHandler对象是在SqlSession对象接收到操作命令后由Configuraion中newStatementHandler方法负责调用的。代码如下:

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;
}

由代码知:statementHandler的创建调用了RoutingStatementHandler的构造方法。

RoutingStatementHandler构造方法,将会根据StatementType决定创建SimpleStatementHandler,PreparedStatementHandler,CallableStatementHandler实例对象。

public enum StatementType {
STATEMENT, PREPARED, CALLABLE
}

构造方法如下所示:

 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());
}
}

而创建三种StatementHandler的构造方法都采用的是BaseStatementHandler的构造方法,其代码如下:

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);
}

这里需要注意的是最后两行代码,创建了parameterHandler和resultSetHandler,这正好和前面所说的StatementHandler、parameterHandler和resultSetHandler是在一起创建的说法保持一致。

创建好handler,执行器的方法会调用prepareStatement方法产生Statment.,代码如下:

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}

此过程中会调用BaseStatementHandler的prepare方法,而在方法中又会调用instantiateStatement方法,通过Connection产生相应的Statment对象。

SqlSession对象之StatementHandler的更多相关文章

  1. mybatis的两个核心对象SqlSessionFactory和SqlSession对象

    mybatis的两个核心对象SqlSessionFactory和SqlSession对象 参见:https://www.cnblogs.com/wxdestiny/p/9743686.html

  2. 对 Service中sqlsession对象的优化

    在本线程中添加object数据,必须在本线程中才能获取出来..其他线程获取不到. public class Test { public static void main(String[] args) ...

  3. SqlSession对象之Executor

    Executor是Mybatis的一个核心接口,每一个SqlSession对象都会拥有一个Executor(执行器对象):这个执行对象负责[增删改查]的具体操作,我们可以简单的将它理解为JDBC中St ...

  4. MyBatis核心对象之StatementHandler

    MyBatis核心对象之StatementHandler StatementHandler ResultHandler ParameterHandler Executor org.apache.iba ...

  5. 使用ThreadLocal管理Mybatis中SqlSession对象

    转自http://blog.csdn.net/qq_29227939/article/details/52029065 public class MybatisUtil { private stati ...

  6. SqlSession对象之ParameterHandler

    上一篇讲了StatementHandler,其中有ParameterHandler(参数处理器)是在StatementHandler被创建时被创建的.下面对ParameterHandler进行说明.其 ...

  7. SqlSession对象之ResultSetHandler

    ResultSetHandler是Mybatis中的另一重要接口,它的代码如下所示: public interface ResultSetHandler { <E> List<E&g ...

  8. 关于Mybatis与Spring整合之后SqlSession与mapper对象之间数量的问题。

    1,sqlsession的真实类型和数量 由于使用spring管理bean,当我们在代码中需要使用这个bean的时候,会首先去容器中找,第一次需要调用MapperFactoryBean的getObje ...

  9. MyBatis常用对象SqlSessionFactory和SqlSession介绍和运用

    学习框架一个比较好的路径阅读源码.本文介绍的SqlSessionFactory和SqlSession.可以通过了解SqlSessionFactory接口和SqlSession接口以及两个的实现类入手, ...

随机推荐

  1. Android---------------解决bug的关键点

    一.抛出异常,打出异常的的堆信息 System.err.print("ceshi");      e.printStackTrace();     Log.i("cesh ...

  2. ICMP与ping:投石问路的侦察兵

    1. ICMP 协议 ICMP全称Internet Control Message Protocol,就是互联网控制报文协议.ping命令就是基于它工作的. ICMP 报文是封装在 IP 包 里面的. ...

  3. DevOps - CI - Jenkins

    Jenkins 开源软件项目,其前身为Hudson,旨在提供一个基于Java开发的开放易用的持续集成工具,用于监控持续重复的工作. 主要用于自动而持续地构建/测试软件项目:监控外部调用执行的工作. 官 ...

  4. Vue.js之下拉列表及选中触发事件

    老早就听说了Vue.js是多么的简单.易学.好用等等,然而我只是粗略的看了下文档,简单的敲了几个例子,仅此而已. 最近由于项目的需要,系统的看了下文档,也学到了一些东西. 废话不多说,这里要说的是下拉 ...

  5. psutil 跨平台根据程序名杀进程

    笔者在项目中遇到过需要根据进程名杀进程的需求,利用python库psutil实现了此功能. 模块地址: https://pypi.python.org/pypi/psutil/   psutil功能 ...

  6. python--使用pickle序列化对象

    pickle序列化对象 如果希望透明地存储 Python 对象,而不丢失其身份和类型等信息,则需要某种形式的对象序列化:它是一个将任意复杂的对象转成对象的文本或二进制表示的过程. 同样,必须能够将对象 ...

  7. [原创]K8 Struts2 Exp 20170310 S2-045(Struts2综合漏洞利用工具)

    工具: K8 Struts2 Exploit组织: K8搞基大队[K8team]作者: K8拉登哥哥博客: http://qqhack8.blog.163.com发布: 2014/7/31 10:24 ...

  8. Centos Android开发环境配置-Android Tools -android list sdk --extended --all

    Centos Android开发环境配置-Android Tools -android  list sdk --extended --all 安装完Android Tools后执行 android   ...

  9. 课程一(Neural Networks and Deep Learning),第三周(Shallow neural networks)—— 1、两层神经网络的单样本向量化表示与多样本向量化表示

    如上图所示的两层神经网络, 单样本向量化:                                                                                ...

  10. windows系统numpy的下载与安装教程

    numpy是一款基于python的功能强大的科学计算包.要安装numpy首先你得先安装python.python的安装非常简单,本人安装的是python3.4. 工具/原料 安装好的python程序 ...