DefaultResultSetHandler的handleRowValues方法中有两个分支,一个用于处理嵌套映射,一个用于处理简单映射,本文讲分析其处理嵌套映射的分支。
handleRowValues方法的定义如下:
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {//是否存在嵌套结果映射
//RowBounds配置校验
ensureNoRowBounds();
//ResultHandler校验
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {//简单映射
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
safeRowBoundsEnabled属性的含义为:是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。默认值为false
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.");
}
}
safeResultHandlerEnabled属性的含义为:是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。默认值为true
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 handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
//创建上下文辅助类
final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
//获取结果集
ResultSet resultSet = rsw.getResultSet();
//跳过指定记录数
skipRows(resultSet, rowBounds);
Object rowValue = previousRowValue;
//判断能否继续映射结果集中剩余的记录
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
//处理鉴别器,确定映射所使用的ResultMap对象
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
//为当前记录创建对应的CacheKey对象
final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
//根据上面创建的从嵌套映射关联的结果对象集合中查询
Object partialObject = nestedResultObjects.get(rowKey);
if (mappedStatement.isResultOrdered()) {//resultOrdered属性为真
if (partialObject == null && rowValue != null) {//partialObject为null表示主结果发生变化
//释放nestedResultObjects的空间
nestedResultObjects.clear();
//保存结果对象
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
//完成对该记录的映射
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
} else {
//完成对该记录的映射
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
if (partialObject == null) {//partialObject为null表示主结果发生变化
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
}
if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
previousRowValue = null;
} else if (rowValue != null) {
previousRowValue = rowValue;
}
}
大致了解其流程之后,下面开始详细分析其中的每一步是如何实现的。
这三个方法在上一篇分析简单映射中已经做了分析。
该方法用于为当前记录生成一个CacheKey对象,用于标识当前记录对应的结果对象,也可以作为缓存的key。
该方法的逻辑大致如下:
或
,则会使用配置的id属性集合创建CacheKey对象。
或
,则使用用户显示配置的属性集合创建CacheKey对象
或
也没有显示配置,则判断ResultMap的type是否为Map类型,如果是Map,则根据所有列名和列值创建CacheKey对象。如果不是Map,使用所有未映射的列名及列值创建CacheKey对象。代码如下:
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.isEmpty()) {
if (Map.class.isAssignableFrom(resultMap.getType())) {//resultMap的类型为Map
//根据所有列名和列值创建CacheKey对象
createRowKeyForMap(rsw, cacheKey);
} else {
//使用所有未映射的列名及列值创建CacheKey对象
createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);
}
} else {
//使用resultMappings中的列名及列值创建CacheKey对象
createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);
}
if (cacheKey.getUpdateCount() < 2) {
return CacheKey.NULL_CACHE_KEY;
}
return cacheKey;
}
获取ResultMapping数组用于生成CacheKey对象。
如果用户配置了
或
,则会使用配置的id属性集合。
如果用户没有配置
或
,则会使用所有配置的属性集合
private List<ResultMapping> getResultMappingsForRowKey(ResultMap resultMap) {
List<ResultMapping> resultMappings = resultMap.getIdResultMappings();
if (resultMappings.isEmpty()) {
resultMappings = resultMap.getPropertyResultMappings();
}
return resultMappings;
}
使用结果集中所有的列名和列值创建CacheKey对象
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);
}
}
}
使用所有未映射的列明及列值创建CacheKey对象
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;
}
}
//metaType对象中存在property属性
if (metaType.findProperty(property, configuration.isMapUnderscoreToCamelCase()) != null) {
String value = rsw.getResultSet().getString(column);
if (value != null) {
cacheKey.update(column);
cacheKey.update(value);
}
}
}
}
逻辑与上面两个方法类似
private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.isSimple()) {
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 || configuration.isReturnInstanceForEmptyRow()) {
cacheKey.update(column);
cacheKey.update(value);
}
}
}
}
}
通过上一篇简单映射可知getRowValue方法用于对记录值的映射,在处理嵌套映射的时候会调用getRowValue的另一个重载方法来完成对记录值的映射。
getRowValue方法如下:
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
final String resultMapId = resultMap.getId();
Object rowValue = partialObject;//主记录对象
if (rowValue != null) {//外层对象存在
final MetaObject metaObject = configuration.newMetaObject(rowValue);
putAncestor(rowValue, resultMapId);
//处理嵌套映射
applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
ancestorObjects.remove(resultMapId);
} else {//外层对象不存在
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//创建外层对象
rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, true)) {//是否处理自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
//处理属性集合
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
//维护当前结果对象和所属resultMap的关系
putAncestor(rowValue, resultMapId);
//处理嵌套映射
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
//移除外层对象
ancestorObjects.remove(resultMapId);
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
if (combinedKey != CacheKey.NULL_CACHE_KEY) {
nestedResultObjects.put(combinedKey, rowValue);
}
}
return rowValue;
}
通过ancestorObjects集合来处理循环引用问题,如果发现存在循环引用则判断需要的对象是否在ancestorObjects集合中存在。如果存在则复用之前已经创建好的对象,如果不存在才会创建新对象。
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);
//获取嵌套的ResultMap对象
final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
if (resultMapping.getColumnPrefix() == null) {
//处理循环依赖的逻辑
Object ancestorObject = ancestorObjects.get(nestedResultMapId);
if (ancestorObject != null) {
if (newObject) {
linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
}
continue;
}
}
final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
//合并两个cacheKey
final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
Object rowValue = nestedResultObjects.get(combinedKey);
boolean knownValue = rowValue != null;
//如果当前属性是集合类型的话,先对该属性进行初始化
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
//判断当前属性是否配置了必须非空,如果为真,则需要创建该属性对应的对象并复制
if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
//递归处理多层嵌套情况
rowValue = getRowValue(rsw, nestedResultMap, combinedKey, 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 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);
}
}
合并两个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;
}
如果当前属性在助对象中是集合类型,则创建一个ArrayList实例,并赋给主对象,防止后面的操作出现空指针异常
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;
}
notNullColumn属性的作用:默认情况下,在至少一个被映射到属性的列不为空时,子对象才会被创建。 你可以在这个属性上指定非空的列来改变默认行为,指定后,Mybatis 将只在这些列非空时才创建一个子对象。可以使用逗号分隔来指定多个列。默认值:未设置(unset)。
在结果集中任何一个非空的列存在值则直接返回true。
private boolean anyNotNullColumnHasValue(ResultMapping resultMapping, String columnPrefix, ResultSetWrapper rsw) throws SQLException {
Set<String> notNullColumns = resultMapping.getNotNullColumns();
if (notNullColumns != null && !notNullColumns.isEmpty()) {
ResultSet rs = rsw.getResultSet();
for (String column : notNullColumns) {
rs.getObject(prependPrefix(column, columnPrefix));
if (!rs.wasNull()) {
return true;
}
}
return false;
} else if (columnPrefix != null) {
for (String columnName : rsw.getColumnNames()) {
if (columnName.toUpperCase().startsWith(columnPrefix.toUpperCase())) {
return true;
}
}
return false;
}
return true;
}