StatementHandler的query方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
这里的StatementHandler具体是什么呢?
这个类其实就是具体执行insert,update,select等的真正实现方法,所以取名叫“(sql)声明处理器”。
那这个handler是怎么构建的呢?
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;
}
RoutingStatementHandler和之前提到CachingExecutor一样,都使用了装饰模式(在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。),这是一个路由的StatementHandler看它的构造方法即可看出。
interceptorChain是什么东西,他是一个链式拦截器,可以定制ExecutorHandler等各种组件的。之后会提到。
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是PreparedStatementHandler(那为什么这里是PreparedStatementHandler,不是Simple呢?我们再之后的配置解析中会提到).所以这里的handler.query方法,是先调用RoutingStatementHandler的query方法,然后通过里面的delegate对象调用PreparedStatementHandler里的query方法。
然后我们看下PreparedStatementHandler方法。
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
通过PreparedStatement 来执行语句,然后用resultSetHandler来处理结果集。
那PreparedStatement 又是什么呢?
我们回到SimpleExecutor中的doQuery方法。发现有这么一句。
stmt = prepareStatement(handler, ms.getStatementLog());
我们看下他的实现:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
这里的handler是上文提到的RoutingStatementHandler。那方法prepare方法就是RoutingStatementHandler里的方法。里面使用delegate调用就应该是PreparedStatementHandler里的prepare方法,但是这个类中没有,那么就在其父类 BaseStatementHandler中。
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement);
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);
}
}
instantiateStatement真正的实现在相对应的StatementHandler里面(这里为PreparedStatementHandler)
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);
}
}
这里前面两个if先别看,看最后一个else。
stringsql = "select * from people p where p.id = ? and p.name = ?";
preparedstatement ps = connection.preparestatement(sql);
ps.setint(1,id);
ps.setstring(2,name);
resultset rs = ps.executequery();
是不是和我们的原生jdbc很接近了。
还有一个parameterize方法不要忽略,这个是设置参数的方法。
他的具体实现是在
org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
再回到PreparedStatementHandler的query方法。其实就是马上调用execute方法(上面是executequery方法)
附:
前面提到的Connection可不是普通的jdbc Connection。而是
org.apache.ibatis.logging.jdbc.ConnectionLogger
在执行这个语句connection.prepareStatement(sql);时,我在ConnectionLogger查找不到prepareStatement,但是他实现了InvocationHandler接口,里面有个invoke方法。
if ("prepareStatement".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
}
发现这里的stmt也不是普通的PreparedStatement,而是PreparedStatementLogger。(JDK动态代理)
而这个方法也实现了InvocationHandler接口。所以上文的ps.execute()方法实际上执行的是PreparedStatementLogger的execute方法。
(这里用了策略设计模式:
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。)
而这些东西都是带有日志功能的类。
最后一步的resultHandler是在DefaultSqlSession的selectList方法里时创建的。
Executor.NO_RESULT_HANDLER
但是发现这个值是空的。实际上这个
return resultSetHandler. handleResultSets(ps);
语句使用的不是参数的resultHandler,这个参数根本没用到。用的是类中的resultSetHandler.
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
而这个是在BaseStatementHandler的构造方法里生成的。而这个往下看就发现只是一个默认的DefaultResultSetHandler。
public List<Object> handleResultSets(Statement stmt) throws SQLException {
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResulSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}