从名字可以知道,StatementHandler主要是用来帮忙处理Statement的,java.sql.Statement
是用来执行SQL并返回结果的对象,StatementHandler提供了几个基础的方法:[prepare、parameterize、batch、update、query]可以看一下基代码:
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();
}
参考JDBC关键部分简单梳理可以知道Statement是执行的关键,而上面StatementHandler的每一个都关乎SQL的执行。有准备Statement的方法,有设置参数的接口,有执行查询更新的接口,也有获取ParameterHandler(参数处理器的接口)
在Executor的执行过程中可以看到有以下步骤:
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
即先通过MapperStatement拿到Connection再调用StatementHandler的prepare方法来准备Statement。
我们可以看一下,StatementHandler
的类图如下:
可以看到StatementHandler接口的实现类有两个,一个BaseStatementHandler是一个抽象类,另外一个RoutingStatementHandler从其名字大概可以知道这个Handler可能是起了一个类似路由的作用,一看其源码也确实如此:
public class RoutingStatementHandler implements StatementHandler {
// 可以看到这里是代理模式的运用,真正执行的是delegate
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 根据StatementType来判断创建不同的Handler
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是从哪儿来的呢,在Executor的执行过程中会通过调用Configuration#newStatementHandler方法创建一个新的StatementHandler,正是在创建StatementHandler的时候会根据不同的类型得到不同的Handler
class Configuration {
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;
}
}
而BaseStatementHandler有一个抽象方法instantiateStatement需要其子类实现:
public abstract class BaseStatementHandler implements StatementHandler {
@Override
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);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}
BaseStatementHandler一共有三个子类:
SimpleStatementHandler
CallableStatementHandler
PreparedStatementHandler
三个方法分别对应
Statement
CallableStatement
PreparedStatement
可以看一下这三个Handler的instantiateStatement方法可以
public class SimpleStatementHandler extends BaseStatementHandler {
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.createStatement();
} else {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
}
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
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() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
}
public class CallableStatementHandler extends BaseStatementHandler {
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareCall(sql);
} else {
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
}
而StatementHandler接口里面的getBoundSql方法,最终会调用到org.apache.ibatis.mapping.MappedStatement#getBoundSql,其代码如下:
// org.apache.ibatis.mapping.MappedStatement#getBoundSql
public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
getBoundSql执行过程可以参考下图:
最后总结一下:
参考Exectuor:Mybatis源码解析04-关键组件Executor可以看到这两个核心组件的类图有相似之处,StatementHandler接口定义核心的方法,抽象类BaseStatementHandler提供一些非子类特有的行为,而子类则去实现自己特有的行为,而RoutingStatementHandler又用到了代理模式,这样让好具体的StatementHandler与上层代码解耦,上层代码面向StatementHandler接口编程即可。