StatementHandler组件封装了对JDBC Statement的操作,其中包括设置Statement对象的fetchSize属性、设置查询超时时间、调用JDBC Statement与数据库交互。在Mybatis源代码中通常搭配Executor使用,如SimpleExecutor类的doQuery方法逻辑所示:
- 通过Configuration构造对应的StatementHandler;
- 通过调用StatementHandler的query方法来完成数据库操作。
@Override
public <E> List<E> doQuery(
MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 调用Configuration构造对应的StatementHandler
StatementHandler handler = configuration.newStatementHandler(
wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
// 通过调用StatementHandler query方法操作数据库
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
org.apache.ibatis.executor.SimpleExecutor#doQuery方法中利用Configuration#newStatementHandler方法来构造一个匹配的StatementHandler,其中newStatementHandler入参解释如下:
- Executor executor,SQL执行器
- MappedStatement mappedStatement,Mapper配置
- Object parameterObject,SQL参数用于构造ParameterHandler
- RowBounds rowBounds,分页
- ResultHandler resultHandler,SQL语句之后后的结构处理器
- BoundSql boundSql,SQL语句包装类
public StatementHandler newStatementHandler(
Executor executor, MappedStatement mappedStatement,
Object parameterObject, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
// 通过RoutingStatementHandler构造函数构造StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(
executor, mappedStatement, parameterObject,
rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(
statementHandler);
return statementHandler;
}
从源代码来看,Configuration#newStatementHandler方法利用RoutingStatementHandler类的构造方法来完成具体类型StatementHandler的构造,RoutingStatementHandler利用所传入的MappedStatement类型来构造对应类型的StatementHandler,具体的类型如org.apache.ibatis.mapping.StatementType枚举类中定义。
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采用了常用的装饰器和模板方法设计模式,如下图所示,StatementHandler接口下有BaseStatementHandler和RoutingStatementHandler两个实现类,其中BaseStatementHandler抽象类实现了一些基础的方法以供子类使用;RoutingStatementHandler类则通过构造方法来路由构造具体的StatementHandler类,以及通过获得的StatementHandler类来装饰实现StatementHandler接口方法。
除了直接实现自StatementHandler接口的BaseStatementHandler和RoutingStatementHandler类以外,还有CallableStatementHandler、PreparedStatementHandler和SimpleStatementHandler三个继承自BaseStatementHandler抽象类的StatementHandler,这三个StatementHandler才是真正实现了对JDBC Statement操作的包装。
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,创建JDBC Statement对象,并完成Statement对象的属性设置。
- parameterize,使用MyBatis中的ParameterHandler组件为PreparedStatement和CallableStatement参数占位符设置值。
- batch,将SQL命令添加到批处量执行列表中。
- update,调用Statement对象的execute()方法执行更新语句,例如UPDATE、INSERT、DELETE语句。
- query,执行查询语句,并使用ResultSetHandler处理查询结果集。
- queryCursor,带游标的查询返回Cursor对象。
- getBoundSql,获取Mapper中配置的SQL信息,BoundSql封装了动态SQL解析后的SQL文本和参数映射信息。
- getParameterHandler,获取ParameterHandler实例。
以小见大
通过org.apache.ibatis.executor.statement.SimpleStatementHandler#query方法来简单窥探一下StatementHandler是如何完成对JDBC Statement的操作,要想完成一次对数据库的操作,需要拥有Statement、ResultHandler结果处理器和BoundSql SQL包装类三件工具,然后通过其相互配合完成一次对数据库的操作。
- 从BoundSql实体中获取对应的SQL语句;
- 将SQL语句传入Statement,然后执行SQL语句;
- 将SQL语句执行结果传入ResultHandler结果处理器,然后处理结果并返回其处理后的结果。
@Override
public <E> List<E> query(
Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}