Mybatis源码分析之结果封装ResultSetHandler和DefaultResultSetHandler
Posted 归田
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis源码分析之结果封装ResultSetHandler和DefaultResultSetHandler相关的知识,希望对你有一定的参考价值。
ResultSetHandler负责处理两件事: (1)处理Statement执行后产生的结果集,生成结果列表 (2)处理存储过程执行后的输出参数ResultSetHandler是一个接口,提供了两个函数分别用来处理普通操作和存储过程的结果,
源码如下:
/**
* @author Clinton Begin
*/
public interface ResultSetHandler
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
ResultSetHandler的具体实现类是DefaultResultSetHandler,其实现的步骤就是将Statement执行后的结果集,按照Mapper文件中配置的ResultType或ResultMap来封装成对应的对象,最后将封装的对象返回即可,
DefaultResultSetHandler源码如下:
//ResultSetHandler具体实现类
public class DefaultResultSetHandler implements ResultSetHandler
private static final Object DEFERED = new Object();
private final Executor executor;
private final Configuration configuration;
private final MappedStatement mappedStatement;
private final RowBounds rowBounds;
private final ParameterHandler parameterHandler;
private final ResultHandler<?> resultHandler;
private final BoundSql boundSql;
private final TypeHandlerRegistry typeHandlerRegistry;
private final ObjectFactory objectFactory;
private final ReflectorFactory reflectorFactory;
// nested resultmaps
// 此Map用来保存当前层级内部的结果对象(一对多关系的多方对象),key为combinedKey
private final Map<CacheKey, Object> nestedResultObjects = new HashMap<CacheKey, Object>();
// 此Map用来保存当前层级的根对象(一对多关系中的一方对象),key为absoluteKey
private final Map<CacheKey, Object> ancestorObjects = new HashMap<CacheKey, Object>();
private final Map<String, String> ancestorColumnPrefix = new HashMap<String, String>();
// multiple resultsets
private final Map<String, ResultMapping> nextResultMaps = new HashMap<String, ResultMapping>();
private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<CacheKey, List<PendingRelation>>();
private static class PendingRelation
public MetaObject metaObject;
public ResultMapping propertyMapping;
public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql,
RowBounds rowBounds)
this.executor = executor;
this.configuration = mappedStatement.getConfiguration();
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.parameterHandler = parameterHandler;
this.boundSql = boundSql;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
this.reflectorFactory = configuration.getReflectorFactory();
this.resultHandler = resultHandler;
//
// HANDLE OUTPUT PARAMETER
//
//调用存储过程返回结果,将结果值放在参数中
@Override
public void handleOutputParameters(CallableStatement cs) throws SQLException
final Object parameterObject = parameterHandler.getParameterObject();
final MetaObject metaParam = configuration.newMetaObject(parameterObject);
final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//循环处理每个参数
for (int i = 0; i < parameterMappings.size(); i++)
final ParameterMapping parameterMapping = parameterMappings.get(i);
//判断参数的模式
if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT)
if (ResultSet.class.equals(parameterMapping.getJavaType()))
handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam);
else
final TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));
private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam) throws SQLException
try
final String resultMapId = parameterMapping.getResultMapId();
final ResultMap resultMap = configuration.getResultMap(resultMapId);
final DefaultResultHandler resultHandler = new DefaultResultHandler(objectFactory);
final ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration);
handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList());
finally
// issue #228 (close resultsets)
closeResultSet(rs);
//
// HANDLE RESULT SETS
//
//对普通查询到的结果转换
@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);
//获得resultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
//这边应该为1吧,一般resultMap为一个
int resultMapCount = resultMaps.size();
//判断是否有resultMap,没有的话抛出异常
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount)
//获得resultMap,实体类和表中数据字段的对应关系
ResultMap resultMap = resultMaps.get(resultSetCount);
//将值设置成对应的resultmap对象
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);
//获得第一个值,并将值打包
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 ResultSetWrapper getNextResultSet(Statement stmt) throws SQLException
// Making this method tolerant of bad JDBC drivers
try
if (stmt.getConnection().getMetaData().supportsMultipleResultSets())
// Crazy Standard JDBC way of determining if there are more results
if (!((!stmt.getMoreResults()) && (stmt.getUpdateCount() == -1)))
ResultSet rs = stmt.getResultSet();
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
catch (Exception e)
// Intentionally ignored.
return null;
//关闭resultSet
private void closeResultSet(ResultSet rs)
try
if (rs != null)
rs.close();
catch (SQLException e)
// ignore
private void cleanUpAfterHandlingResultSet()
nestedResultObjects.clear();
ancestorColumnPrefix.clear();
//校验结果的resultMap,如果没有的话就抛出异常
private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount)
if (rsw != null && resultMapCount < 1)
throw new ExecutorException("A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId()
+ "'. It's likely that neither a Result Type nor a Result Map was specified.");
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
if (resultHandler == null)
// 创建一个新对象
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//将查询到的每个字段和Bean实体中的属性对应起来,生成一个Result对象
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//结果映射对象及值添加到multipleResults中
multipleResults.add(defaultResultHandler.getResultList());
else
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
finally
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
@SuppressWarnings("unchecked")
private List<Object> collapseSingleResultList(List<Object> multipleResults)
return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
//
// HANDLE ROWS FOR SIMPLE RESULTMAP
//
//操作列值
private 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);
//判断记录行数
private void ensureNoRowBounds()
if (configuration.isSafeRowBoundsEnabled() && rowBounds != null && (rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT || rowBounds.getOffset() > RowBounds.NO_ROW_OFFSET))
throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. "
+ "Use safeRowBoundsEnabled=false setting to bypass this check.");
protected void checkResultHandler()
if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.isResultOrdered())
throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. "
+ "Use safeResultHandlerEnabled=false setting to bypass this check "
+ "or ensure your statement returns ordered data and set resultOrdered=true on it.");
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
//跳过RowBounds设置的offset值
skipRows(rsw.getResultSet(), rowBounds);
//判断数据是否小于limit,如果小于limit的话就不断的循环取值
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next())
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
Object rowValue = getRowValue(rsw, discriminatedResultMap);
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException
if (parentMapping != null)
linkToParents(rs, parentMapping, rowValue);
else
callResultHandler(resultHandler, resultContext, rowValue);
@SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue)
resultContext.nextResultObject(rowValue);
((ResultHandler<Object>)resultHandler).handleResult(resultContext);
private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException
//判断数据是否小于limit,小于返回true
return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
//跳过不需要的行,应该就是rowbounds设置的limit和offset
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
//跳过RowBounds中设置的offset条数据
for (int i = 0; i < rowBounds.getOffset(); i++)
rs.next();
//
// GET VALUE FROM ROW FOR SIMPLE RESULT MAP
//
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//结果的映射对象
Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType()))
final MetaObject metaObject = configuration.newMetaObject(resultObject);
boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
if (shouldApplyAutomaticMappings(resultMap, false))
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
resultObject = foundValues ? resultObject : null;
return resultObject;
return resultObject;
//用于判断是否是bean实体变量和表中字段自动映射
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested)
if (resultMap.getAutoMapping() != null)
return resultMap.getAutoMapping();
else
if (isNested)
return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
else
return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
//
// PROPERTY MAPPINGS
//
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;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings)
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null)
// the user added a column attribute to a nested result map, ignore it
column = null;
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null)
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
// issue #377, call setter on nulls
if (value != DEFERED
&& property != null
&& (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())))
metaObject.setValue(property, value);
if (value != null || value == DEFERED)
foundValues = true;
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); // TODO is that OK?
return DEFERED;
else
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
return typeHandler.getResult(rs, column);
//
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
//获得列名
for (String columnName : unmappedColumnNames)
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty())
// When columnPrefix is specified,
// ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix))
propertyName = columnName.substring(columnPrefix.length());
else
continue;
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property))
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType))
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
final Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
// issue #377, call setter on nulls
if (value != null || configuration.isCallSettersOnNulls())
if (value != null || !propertyType.isPrimitive())
metaObject.setValue(property, value);
foundValues = true;
return foundValues;
// MULTIPLE RESULT SETS
private void linkToParents(ResultSet rs, ResultMapping parentMapping, Object rowValue) throws SQLException
CacheKey parentKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getForeignColumn());
List<PendingRelation> parents = pendingRelations.get(parentKey);
if (parents != null)
for (PendingRelation parent : parents)
if (parent != null && rowValue != null)
linkObjects(parent.metaObject, parent.propertyMapping, rowValue);
private void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject, ResultMapping parentMapping) throws SQLException
CacheKey cacheKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getColumn());
PendingRelation deferLoad = new PendingRelation();
deferLoad.metaObject = metaResultObject;
deferLoad.propertyMapping = parentMapping;
List<PendingRelation> relations = pendingRelations.get(cacheKey);
// issue #255
if (relations == null)
relations = new ArrayList<DefaultResultSetHandler.PendingRelation>();
pendingRelations.put(cacheKey, relations);
relations.add(deferLoad);
ResultMapping previous = nextResultMaps.get(parentMapping.getResultSet());
if (previous == null)
nextResultMaps.put(parentMapping.getResultSet(), parentMapping);
else
if (!previous.equals(parentMapping))
throw new ExecutorException("Two different properties are mapped to the same resultSet");
private CacheKey createKeyForMultipleResults(ResultSet rs, ResultMapping resultMapping, String names, String columns) throws SQLException
CacheKey cacheKey = new CacheKey();
cacheKey.update(resultMapping);
if (columns != null && names != null)
String[] columnsArray = columns.split(",");
String[] namesArray = names.split(",");
for (int i = 0 ; i < columnsArray.length ; i++)
Object value = rs.getString(columnsArray[i]);
if (value != null)
cacheKey.update(namesArray[i]);
cacheKey.update(value);
return cacheKey;
//
// INSTANTIATION & CONSTRUCTOR MAPPING
//
//创建结果对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException
final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
final List<Object> constructorArgs = new ArrayList<Object>();
//结果对象
final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType()))
//获取配置的resultMap的字段与表中数据的映射关系
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings)
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy())
return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
return resultObject;
//创建结果对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException
//结果类型
final Class<?> resultType = resultMap.getType();
//通过反射获取结果对象
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (typeHandlerRegistry.hasTypeHandler(resultType))
//基本类型
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
else if (!constructorMappings.isEmpty())
//有参数构造函数
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
else if (resultType.isInterface() || metaType.hasDefaultConstructor())
//普通的bean类型
return objectFactory.create(resultType);
else if (shouldApplyAutomaticMappings(resultMap, false))
//自动映射
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
throw new ExecutorException("Do not know how to create an instance of " + resultType);
//有参数构造函数
Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
boolean foundValues = false;
for (ResultMapping constructorMapping : constructorMappings)
final Class<?> parameterType = constructorMapping.getJavaType();
final String column = constructorMapping.getColumn();
final Object value;
try
if (constructorMapping.getNestedQueryId() != null)
value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
else if (constructorMapping.getNestedResultMapId() != null)
final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
value = getRowValue(rsw, resultMap);
else
final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
catch (ResultMapException e)
throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
catch (SQLException e)
throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
//自动映射
private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs,
String columnPrefix) throws SQLException
for (Constructor<?> constructor : resultType.getDeclaredConstructors())
if (typeNames(constructor.getParameterTypes()).equals(rsw.getClassNames()))
boolean foundValues = false;
for (int i = 0; i < constructor.getParameterTypes().length; i++)
Class<?> parameterType = constructor.getParameterTypes()[i];
String columnName = rsw.getColumnNames().get(i);
TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
Object value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(columnName, columnPrefix));
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
private List<String> typeNames(Class<?>[] parameterTypes)
List<String> names = new ArrayList<String>();
for (Class<?> type : parameterTypes)
names.add(type.getName());
return names;
//创建原型结果
private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException
final Class<?> resultType = resultMap.getType();
final String columnName;
if (!resultMap.getResultMappings().isEmpty())
final List<ResultMapping> resultMappingList = resultMap.getResultMappings();
final ResultMapping mapping = resultMappingList.get(0);
columnName = prependPrefix(mapping.getColumn(), columnPrefix);
else
columnName = rsw.getColumnNames().get(0);
final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
return typeHandler.getResult(rsw.getResultSet(), columnName);
//
// NESTED QUERY
//
private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException
final String nestedQueryId = constructorMapping.getNestedQueryId();
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null)
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = constructorMapping.getJavaType();
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
value = resultLoader.loadResult();
return value;
//得到嵌套查询值
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException
final String nestedQueryId = propertyMapping.getNestedQueryId();
final String property = propertyMapping.getProperty();
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null)
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = propertyMapping.getJavaType();
if (executor.isCached(nestedQuery, key))
//如果已经有一级缓存了,则延迟加载(实际上deferLoad方法中可以看到则是立即加载)
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
value = DEFERED;
else
//否则lazyLoader.addLoader 需要延迟加载则addLoader
//或者ResultLoader.loadResult 不需要延迟加载则立即加载
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
if (propertyMapping.isLazy())
lazyLoader.addLoader(property, metaResultObject, resultLoader);
value = DEFERED;
else
value = resultLoader.loadResult();
return value;
private Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException
if (resultMapping.isCompositeResult())
return prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix);
else
return prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix);
private Object prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException
final TypeHandler<?> typeHandler;
if (typeHandlerRegistry.hasTypeHandler(parameterType))
typeHandler = typeHandlerRegistry.getTypeHandler(parameterType);
else
typeHandler = typeHandlerRegistry.getUnknownTypeHandler();
return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException
final Object parameterObject = instantiateParameterObject(parameterType);
final MetaObject metaObject = configuration.newMetaObject(parameterObject);
boolean foundValues = false;
for (ResultMapping innerResultMapping : resultMapping.getComposites())
final Class<?> propType = metaObject.getSetterType(innerResultMapping.getProperty());
final TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(propType);
final Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));
// issue #353 & #560 do not execute nested query if key is null
if (propValue != null)
metaObject.setValue(innerResultMapping.getProperty(), propValue);
foundValues = true;
return foundValues ? parameterObject : null;
private Object instantiateParameterObject(Class<?> parameterType)
if (parameterType == null)
return new HashMap<Object, Object>();
else
return objectFactory.create(parameterType);
//
// DISCRIMINATOR
//
public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException
Set<String> pastDiscriminators = new HashSet<String>();
Discriminator discriminator = resultMap.getDiscriminator();
while (discriminator != null)
final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
if (configuration.hasResultMap(discriminatedMapId))
resultMap = configuration.getResultMap(discriminatedMapId);
Discriminator lastDiscriminator = discriminator;
discriminator = resultMap.getDiscriminator();
if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId))
break;
else
break;
return resultMap;
private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException
final ResultMapping resultMapping = discriminator.getResultMapping();
final TypeHandler<?> typeHandler = resultMapping.getTypeHandler();
return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
private String prependPrefix(String columnName, String prefix)
if (columnName == null || columnName.length() == 0 || prefix == null || prefix.length() == 0)
return columnName;
return prefix + columnName;
//
// HANDLE NESTED RESULT MAPS
//
//生成嵌套对象值
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException
//创建默认结果上下文
final DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
//跳过rowBounds指定offset行偏移量
skipRows(rsw.getResultSet(), rowBounds);
Object rowValue = null;
//如何定义应该处理:上下文没有主动停止,结果集还有记录,且上下文中结果对象数量不足时,应该继续处理
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next())
// 解决鉴别过的结果映射,逻辑如下:
// 获取结果映射中的鉴别器,通过鉴别指定字段通过配置对象获取对应的另一个结果映射,循环往复,
// 直到找不到鉴别器为止,返回最终的结果映射
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
// 创建缓存key,如何创建?
final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
// 缓存中获取结果对象
Object partialObject = nestedResultObjects.get(rowKey);
// issue #577 && #542
if (mappedStatement.isResultOrdered())
if (partialObject == null && rowValue != null)
nestedResultObjects.clear();
// 保存结果对象
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
// 获取行值
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);
else
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);
if (partialObject == null)
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds))
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
//
// GET VALUE FROM ROW FOR NESTED RESULT MAP
//
//
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, CacheKey absoluteKey, String columnPrefix, Object partialObject) throws SQLException
final String resultMapId = resultMap.getId();
Object resultObject = partialObject;
if (resultObject != null)
final MetaObject metaObject = configuration.newMetaObject(resultObject);
putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
ancestorObjects.remove(absoluteKey);
else
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
resultObject = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType()))
final MetaObject metaObject = configuration.newMetaObject(resultObject);
boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
if (shouldApplyAutomaticMappings(resultMap, true))
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
ancestorObjects.remove(absoluteKey);
foundValues = lazyLoader.size() > 0 || foundValues;
resultObject = foundValues ? resultObject : null;
if (combinedKey != CacheKey.NULL_CACHE_KEY)
nestedResultObjects.put(combinedKey, resultObject);
return resultObject;
private void putAncestor(CacheKey rowKey, Object resultObject, String resultMapId, String columnPrefix)
if (!ancestorColumnPrefix.containsKey(resultMapId))
ancestorColumnPrefix.put(resultMapId, columnPrefix);
ancestorObjects.put(rowKey, resultObject);
//
// NESTED RESULT MAP (JOIN MAPPING)
//
private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject)
boolean foundValues = false;
for (ResultMapping resultMapping : resultMap.getPropertyResultMappings())
final String nestedResultMapId = resultMapping.getNestedResultMapId();
if (nestedResultMapId != null && resultMapping.getResultSet() == null)
try
final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
CacheKey rowKey = null;
Object ancestorObject = null;
if (ancestorColumnPrefix.containsKey(nestedResultMapId))
rowKey = createRowKey(nestedResultMap, rsw, ancestorColumnPrefix.get(nestedResultMapId));
ancestorObject = ancestorObjects.get(rowKey);
if (ancestorObject != null)
if (newObject)
linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
else
rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
Object rowValue = nestedResultObjects.get(combinedKey);
boolean knownValue = (rowValue != null);
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw.getResultSet()))
rowValue = getRowValue(rsw, nestedResultMap, combinedKey, rowKey, columnPrefix, rowValue);
if (rowValue != null && !knownValue)
linkObjects(metaObject, resultMapping, rowValue);
foundValues = true;
catch (SQLException e)
throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'. Cause: " + e, e);
return foundValues;
private String getColumnPrefix(String parentPrefix, ResultMapping resultMapping)
final StringBuilder columnPrefixBuilder = new StringBuilder();
if (parentPrefix != null)
columnPrefixBuilder.append(parentPrefix);
if (resultMapping.getColumnPrefix() != null)
columnPrefixBuilder.append(resultMapping.getColumnPrefix());
return columnPrefixBuilder.length() == 0 ? null : columnPrefixBuilder.toString().toUpperCase(Locale.ENGLISH);
private boolean anyNotNullColumnHasValue(ResultMapping resultMapping, String columnPrefix, ResultSet rs) throws SQLException
Set<String> notNullColumns = resultMapping.getNotNullColumns();
boolean anyNotNullColumnHasValue = true;
if (notNullColumns != null && !notNullColumns.isEmpty())
anyNotNullColumnHasValue = false;
for (String column: notNullColumns)
rs.getObject(prependPrefix(column, columnPrefix));
if (!rs.wasNull())
anyNotNullColumnHasValue = true;
break;
return anyNotNullColumnHasValue;
private ResultMap getNestedResultMap(ResultSet rs, String nestedResultMapId, String columnPrefix) throws SQLException
ResultMap nestedResultMap = configuration.getResultMap(nestedResultMapId);
return resolveDiscriminatedResultMap(rs, nestedResultMap, columnPrefix);
//
// UNIQUE RESULT KEY
//
private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException
final CacheKey cacheKey = new CacheKey();
cacheKey.update(resultMap.getId());
List<ResultMapping> resultMappings = getResultMappingsForRowKey(resultMap);
if (resultMappings.size() == 0)
if (Map.class.isAssignableFrom(resultMap.getType()))
createRowKeyForMap(rsw, cacheKey);
else
createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);
else
createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);
return cacheKey;
private CacheKey combineKeys(CacheKey rowKey, CacheKey parentRowKey)
if (rowKey.getUpdateCount() > 1 && parentRowKey.getUpdateCount() > 1)
CacheKey combinedKey;
try
combinedKey = rowKey.clone();
catch (CloneNotSupportedException e)
throw new ExecutorException("Error cloning cache key. Cause: " + e, e);
combinedKey.update(parentRowKey);
return combinedKey;
return CacheKey.NULL_CACHE_KEY;
private List<ResultMapping> getResultMappingsForRowKey(ResultMap resultMap)
List<ResultMapping> resultMappings = resultMap.getIdResultMappings();
if (resultMappings.size() == 0)
resultMappings = resultMap.getPropertyResultMappings();
return resultMappings;
private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException
for (ResultMapping resultMapping : resultMappings)
if (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null)
// Issue #392
final ResultMap nestedResultMap = configuration.getResultMap(resultMapping.getNestedResultMapId());
createRowKeyForMappedProperties(nestedResultMap, rsw, cacheKey, nestedResultMap.getConstructorResultMappings(),
prependPrefix(resultMapping.getColumnPrefix(), columnPrefix));
else if (resultMapping.getNestedQueryId() == null)
final String column = prependPrefix(resultMapping.getColumn(), columnPrefix);
final TypeHandler<?> th = resultMapping.getTypeHandler();
List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
// Issue #114
if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
final Object value = th.getResult(rsw.getResultSet(), column);
if (value != null)
cacheKey.update(column);
cacheKey.update(value);
private void createRowKeyForUnmappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, String columnPrefix) throws SQLException
final MetaClass metaType = MetaClass.forClass(resultMap.getType(), reflectorFactory);
List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String column : unmappedColumnNames)
String property = column;
if (columnPrefix != null && !columnPrefix.isEmpty())
// When columnPrefix is specified, ignore columns without the prefix.
if (column.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix))
property = column.substring(columnPrefix.length());
else
continue;
if (metaType.findProperty(property, configuration.isMapUnderscoreToCamelCase()) != null)
String value = rsw.getResultSet().getString(column);
if (value != null)
cacheKey.update(column);
cacheKey.update(value);
private void createRowKeyForMap(ResultSetWrapper rsw, CacheKey cacheKey) throws SQLException
List<String> columnNames = rsw.getColumnNames();
for (String columnName : columnNames)
final String value = rsw.getResultSet().getString(columnName);
if (value != null)
cacheKey.update(columnName);
cacheKey.update(value);
private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue)
final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
if (collectionProperty != null)
final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
targetMetaObject.add(rowValue);
else
metaObject.setValue(resultMapping.getProperty(), rowValue);
private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject)
final String propertyName = resultMapping.getProperty();
Object propertyValue = metaObject.getValue(propertyName);
if (propertyValue == null)
Class<?> type = resultMapping.getJavaType();
if (type == null)
type = metaObject.getSetterType(propertyName);
try
if (objectFactory.isCollection(type))
propertyValue = objectFactory.create(type);
metaObject.setValue(propertyName, propertyValue);
return propertyValue;
catch (Exception e)
throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'. Cause: " + e, e);
else if (objectFactory.isCollection(propertyValue.getClass()))
return propertyValue;
return null;
以上是关于Mybatis源码分析之结果封装ResultSetHandler和DefaultResultSetHandler的主要内容,如果未能解决你的问题,请参考以下文章
mybatis 那么垃圾 为什么有人用,除了封装一下结果集 我不知道他还干了什么。