上一期,我们分析了sql语句的创建过程,接下来,咱们进入到下一个特别重要的对象创建,statement对象的创建
StatementHandler 是一个非常核心接口,不仅要创建合适statement对象,再将参数放到statement对象中,执行sql,处理结果集.可以说这些步骤都是在statementHandle中
实现的,接下来咱们跟着源码一起来分析,还是以查询为例,接着上期的selectOne分析
@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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建路由statement
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//应用插件到StatementHandler上
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 根据 StatementType 创建不同的 StatementHandler
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());
}
}
再来看一下statement的uml图
image.png
接下来,我们需要设置参数到sql中了,JDBC 提供了三种 Statement 接口,分别是 Statement、PreparedStatement 和 CallableStatement。他们的uml图为:
image.png
statement:提供执行的sql语句
PreparedStatement:对输入参数执行动态替换,也就是将之前的?替换为具体的参数
CallableStatement:用于保存存储过程输出的结果
我们来来分析PreparedStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取连接
Connection connection = getConnection(statementLog);
//创建 Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//设置参数
handler.parameterize(stmt);
return stmt;
}
@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);
//设置fetchSize
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);
}
}
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
// 根据条件调用不同的 prepareStatement 方法创建 PreparedStatement
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);
}
}
statement创建就是这样,重点看下参数是怎样设置进去的
handler.parameterize(stmt)
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//从boundSql中获取参数映射的集合
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();
// 检测 BoundSql 的 additionalParameters 是否包含 propertyName
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();
}
try {
//typeHandler对ParameterHandler 设置参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
我们来执行一条根据主键查询的sql语句,然后debug运行,看看参数如何设置进去的
<!-- 根据PrimaryKey获取对象-->
<select id="selectByPrimaryKey" resultMap="base_result_map" >
select <include refid="base_column_list" /> from `ryx_account` where `id`=#{id}
</select>
image.png
每一个?都会在parameterMappings集合中生成一个对象属性,执行完之后,一条正正的sql就形成了
image.png
当我们执行完sql后就要开始处理查询结果了结果集的处理工作由结果集处理器 ResultSetHandler 执行。ResultSetHandler 是一个接口,它只有一个实现类 DefaultResultSetHandler。结果集的处理入口方法是 handleResultSets,下面来看一下该方法的实现。
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
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.getResultSets();
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);
}
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
//获取结果集
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
//处理结果集方法
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
//处理存储过程的逻辑,多结果集
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
//这里有个细节注意下,我们可以实现resultHandler接口,处理我们自己定义的结果集类型
if (resultHandler == null) {
//构建默认的结果集处理对象
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//处理行值方法
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
// 处理结果集的行数据
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
//处理行值方法
//处理行值方法
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
//处理结果集映射
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//处理简单结果映射
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
这里注意啊一下,resultHandle是一个接口,我们可以实现自己的类,用来自定义结果集,map,list等等
image.png
我们这里已简单结果映射分析为主,
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
// 根据 RowBounds 定位到指定行记录
skipRows(rsw.getResultSet(), rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
// 获取经过鉴别器处理后的 ResultMap
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
// 从 resultSet 中获取结果
Object rowValue = getRowValue(rsw, discriminatedResultMap);
// 存储结果
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
rs.absolute(rowBounds.getOffset());
}
} else {
for (int i = 0; i < rowBounds.getOffset(); i++) {
//根据循环去找下一个值,如果offest的值很大,效率就会很低,所以mybatis自带的分页插件效率很低哦
rs.next();
}
}
}
接下来分析getRowValue方法(获取行值)
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建结果对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
// 是否应该自动映射结果集
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 进行自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
// 根据resultMap节点中配置的映射关系进行映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
这个方法一共有三段逻辑,分别是
1: 创建结果对象
2:是否应该自动映射结果集
3:根据resultMap节点中配置的映射关系进行映射
我们来一一分析下
1:创建结果对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
//重置之前的映射结果
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
final List<Object> constructorArgs = new ArrayList<Object>();
//调用重载方法创建结果对象
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = (resultObject != null && !constructorArgTypes.isEmpty()); // set current mapping result
return resultObject;
}
2:是否应该自动映射结果集,这个就不分析了,伙伴们可以自行分析下
3:根据resultMap节点中配置的映射关系进行映射
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 获取 UnMappedColumnAutoMapping 列表
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// 通过 TypeHandler 从结果集中获取指定列的数据
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// 通过元信息对象设置 value 到实体类对象的指定字段上
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
// 从缓存中获取 UnMappedColumnAutoMapping 列表
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
// 缓存未命中
if (autoMapping == null) {
autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
// 从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
// 获取不包含列名前缀的属性名
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
// 将下划线形式的列名转成驼峰式,比如 AUTHOR_NAME -> authorName
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {
// 检测当前属性是否存在于 resultMap 中
if (resultMap.getMappedProperties().contains(property)) {
continue;
}
// 获取属性的类型
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
// 获取类型处理器
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
// 封装上面获取到的信息autoMapping集合中
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else {
//若 property 为空,或实体类中无 property 属性,此时无法完成
//列名与实体类属性建立映射关系。默认什么都不做
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}
// 写入缓存
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
总结下以上步骤
从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名
遍历获取到的列名列表
若列名包含列名前缀,则移除列名前缀,得到属性名
将下划线形式的列名转成驼峰式
获取属性类型
获取类型处理器
创建UnMappedColumnAutoMapping对象
我们主要看一下第一个步骤
public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
//获取未映射的列名
List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
if (unMappedColumnNames == null) {
//加载映射和未映射的列名
loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
}
return unMappedColumnNames;
}
private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
List<String> mappedColumnNames = new ArrayList<String>();
List<String> unmappedColumnNames = new ArrayList<String>();
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
for (String columnName : columnNames) {
final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
if (mappedColumns.contains(upperColumnName)) {
mappedColumnNames.add(upperColumnName);
} else {
unmappedColumnNames.add(columnName);
}
}
mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
}
接下来分析一下 MyBatis 是如何将结果集中的数据填充到已映射的实体类字段中的
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
// 获取映射的列名
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
// 获取 ResultMapping
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// 拼接列名前缀,得到完整列名
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
column = null;
}
/*
* 下面的 if 分支由三个或条件组合而成,三个条件的含义如下:
* 条件一:检测 column 是否为 {prop1=col1, prop2=col2} 形式,该
* 种形式的 column 一般用于关联查询
* 条件二:检测当前列名是否被包含在已映射的列名集合中,若包含则可进行数据集映射操作
* 条件三:多结果集相关,暂不分析
*/
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
// 从结果集中获取指定列的数据
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
// 若获取到的值为 DEFERED,则延迟加载该值
} else if (value == DEFERED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property)
.isPrimitive())) {
// 将获取到的值设置到实体类对象中
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
// 获取关联查询结果,下一节分析
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping);
return DEFERED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
// 拼接前缀
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 从 ResultSet 中获取指定列的值
return typeHandler.getResult(rs, column);
}
}
网友评论