StatementHandler
一、StatementHandler和Statement
1.1 StatementHandler接口
- StatementHandler是Mybatis四大对象之一,它完成Mybatis最核心的工作,它封装了Statement的相关操作来完成数据库的访问,也是Executor组件的基础。包括创建Statement对象,为sql绑定参数,执行sql,结果集映射等。它是一个接口,使用不同的实现类来处理不同的情况,并且采用了模板模式,使用BaseStatementHandler来完成基本骨架,子类继承然后实现具体方法细节。
- 在了解StatementHandler之前,我们先了解一下Statement。
1.2 Statement接口
- Statement是JDK的java.sql包下的接口,JDK源码注释表示Statement是用于执行sql语句并返回结果集的对象,接口定义了很多关于数据访问和处理的方法。
//The object used for executing a static SQL statement and returning the results it produces
public interface Statement extends Wrapper, AutoCloseable {
//执行sql,返回单个结果集
ResultSet executeQuery(String sql) throws SQLException;
//执行增、删或者改的sql语句
int executeUpdate(String sql) throws SQLException;
//关闭Statement
void close() throws SQLException;
//执行sql,可返回多个结果集
boolean execute(String sql) throws SQLException;
//其他方法...省略...
}
- Statement接口的主要实现类是PreparedStatement,另外还有CallableStatemen和其他实现类。
- 我们简单了解Statement的功能即可,知道Statement在访问数据库过程中的作用,后面的StatementHandler实现的相关功能则是围绕Statement的。
二、StatementHandler
2.1 接口
- StatementHandler接口封装了关于Statement的很多操作方法,比如获取Statement的prepare方法,处理参数的parameterize方法(处理参数内部调用的还是Statement自身的方法)、查询或者更新方法query和update等。简而言之StatementHandler的存在让很多原本需要访问Statement的操作现在只需要访问StatementHandler,不过本质上还是访问Statement,只不过做了一些封装和简化。
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();
}
2.2 实现类
实现类 |
描述 |
BaseStatementHandler |
不同子类的抽象父类,模板模式的骨架方法实现,同时也实现了大部分接口方法,简化子类的实现 |
RoutingStatementHandler |
静态代理,本身没有额外功能,Execuotr中实例化的就是它,它会根据具体的环境实例化不同的子类 |
SimpleStatementHandler |
使用statement访问数据库,无需参数化 |
PreparedStatementHandler |
使用预编译的PrepareStatement访问数据库 |
CallableStatementHandler |
存储过程处理 |
- StatementHandler中使用了模板模式。举一个模板模式体现的例子:在StatementHandler中定义了prepare方法获取Statement,在BaseStatementHandler中完成了prepare方法的主体流程,但是真正创建Statement实例在BaseStatementHandler没有实现,而是通过一个abstract的方法instantiateStatement交给子类去实现,因为不同的三个子类创建Statement的逻辑是不一样的,这就是模板模式的体现。另外在BaseStatementHandler中也实现了StatementHandler中的很多方法,这样三个真正的子类只需要实现部分方法即可,大部分方法都在BaseStatementHandler中实现了,通过继承获取,简化了部分代码。
三、实现类分析
3.1 BaseStatementHandler
- BaseStatementHandler是StatementHandler的实现类,也是其他几个实现类的类抽象父类,实现类主要的一些方法,其中包括获取Statement的prepare方法的主流程,但是把实例化Statement的步骤通过抽象方法instantiateStatement交给不同的子类实现。
/**
* StatementHandler创建Statement的主要逻辑都在这里,StatementHandler的其他子类并没有实现这个方法,直接从BaseStatementHandler
* 继承获得到,但是Statement的真正实例化是在抽象方法instantiateStatement中,留给不同的子类实现的
* */
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//创建Statement主流程
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);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
3.2 RoutingStatementHandler
- RoutingStatementHandler没有实现额外功能,它通过静态代理模式起到了一个路由的功能,根据不同的场景选择合适的StatementHandler。它的内部持有一个StatementHandler对象delegate,在构造方法总实例化内部的delegate,然后实现了StatementHandler的全部方法,实际上都是调用delegate来执行,自己就像一个路由角色,代码如下:
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
//构造方法,在构造方法总实例化内部的delegate,然后实现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());
}
}
//然后继承自StatementHandler的方法实际上都是调用delegate来执行
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
//省略其他....
}
- 在映射文件的sql语句中可以设置StatementType,在RoutingStatementHandler里面到底是要哪一个,取决于select里面配置的是哪一种。MappedStatement是sql语句映射的数据结构,在MappedStatement中保存了StatementType,RoutingStatementHandler实例化真实的子类时会根据MappedStatement.getStatementType()进行判断。
public enum StatementType {
STATEMENT, PREPARED, CALLABLE
}
3.3 PreparedStatementHandler
- StatementType默认是PREPARED,下面通过一次查询的调试步骤看看获取Statement的过程。
- 第一步:SimpleExecutor的doQuery方法中:configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);创建StatementHandler创建的就是RoutingStatementHandler。
- 第二步:在SimpleExecutor#prepareStatement方法中,调用RoutingStatementHandler的prepare方法。
- 第三步:在RoutingStatementHandler的prepare方法中,调用的是PreparedStatementHandler的prepare方法。(默认情况下使用的就是PreparedStatementHandler,此时RoutingStatementHandler内部的代理是PreparedStatementHandler
- 第四步:走到抽象父类BaseStatementHandler的prepare方法,这是创建Statement的骨架方法,但是instantiateStatement实例化的过程会走到子类PreparedStatementHandler
- 第五步:走到PreparedStatementHandler的instantiateStatement方法实例化Statement成功。
- 第六步:最后一步一步返回到SimpleExecutor类的doQuery方法进行数据库后续的操作。流程图可以参考4.1小节
3.4 SimpleStatementHandler
- SimpleStatementHandler和PreparedStatementHandler相比最大的区别是前者不会对sql语句进行预编译,因此在实例化Statement的核心方法instantiateStatement和PreparedStatementHandler有所不同
/**
*
* SimpleStatementHandler不会预编译sql语句,返回Statement
* */
@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();
}
}
3.5 CallableStatementHandler
- CallableStatementHandler主要处理存储过程,不做重点分析。
四、小结
4.1 流程图
4.2 小结
- StatementHandler对Statement的相关操作进行了封装,他有有一个抽象子类BaseStatementHandler实现了大部分方法和主要方法的核心流程,将不同情况下Statement的实例化步骤通过抽象方法交给不同的子类实现
- BaseStatementHandler是三种不同子类SimpleStatementHandler、PreparedStatementHandler和CallableStatementHandler的抽象父类,PreparedStatementHandler是默认的情况,会预编译sql语句,SimpleStatementHandler则不会
- StatementType有3中切换模式,在Mybatis的映射文件的sql节点可以配置
- 不管是哪一种Statement,他都是被Executor组件使用,其实Executor实例化的是RoutingStatementHandler,RoutingStatementHandler内部实例化不同的子类对象。Executor组件是执行sql操作的组件,但是实际上Executor还是通过StatementHandler来操作Statement,自上而下来看,本质上还是JDBC的访问。
五、参考