背景
同事在使用resultMap时,内嵌了collections元素,并且collections里面还内嵌一层collections。模拟代码如下:
<resultMap id="base" type="my.mybatis.entity.User">\\用户
<id property="id" column="id"/>
<collection property="roles" ofType="my.mybatis.entity.Role" columnPrefix="role_">\\权限
<id property="id" column="id"/>
<collection property="menus" ofType="my.mybatis.entity.Menu" columnPrefix="menu_">\\菜单
<id property="id" column="id"/>
</collection>
</collection>
</resultMap>
<sql id="Base_Column_List">
user.id,
role.id as role_id,
menu.id as menu_id
</sql>
但是发现最内层的menus属性一直都为空,无法映射到entity里。在网上找了一下,发现没有什么答案,就决定看mybatis的源码,找一下对象映射的逻辑。
先讲无法映射的原因
由于第一层collection写明了columnPrefix=“role_”,第二层collection写明了columnPrefix=“menu_”。
第二层的前缀会继承第一层的字段映射前缀。
即 mybatis的结果集映射时 会找 role_menu_id的字段
所以正确的sql字段写法为
<sql id="Base_Column_List">
user.id,
role.id as role_id,
menu.id as role_menu_id
</sql>
源码逻辑
public class DefaultResultSetHandler implements ResultSetHandler {
....................
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);
}
}
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap,
ResultHandler<?> resultHandler, RowBounds rowBounds,
ResultMapping parentMapping) throws SQLException {
final DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
skipRows(rsw.getResultSet(), rowBounds);
Object rowValue = previousRowValue;
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
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, null, partialObject);
} else {
rowValue = getRowValue(rsw, discriminatedResultMap, 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());
previousRowValue = null;
} else if (rowValue != null) {
previousRowValue = rowValue;
}
}
//
// GET VALUE FROM ROW FOR NESTED RESULT MAP
//
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, columnPrefix);
applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
ancestorObjects.remove(resultMapId);
} else {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//使用反射实例化entity
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;
putAncestor(rowValue, resultMapId, columnPrefix);
//映射嵌套的结果集
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;
}
/**
* @param metaObject 上一层对象的封装类
**/
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();
//如果是嵌套的ResultMap,即collection元素
if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
try {
//组成嵌套 prefix
final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
//取的嵌套的ResultMap,然后 重新走getObjectValue方法,对ResultMap的resultMapping赋值
final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
if (resultMapping.getColumnPrefix() == null) {
// try to fill circular reference only when columnPrefix
// is not specified for the nested result map (issue #215)
//寻找第一层父对象
Object ancestorObject = ancestorObjects.get(nestedResultMapId);
if (ancestorObject != null) {
if (newObject) {//是不是 嵌套的新对象
linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
}
continue;
}
}
final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
//找嵌套对象,如果为null的话,会在getRowValue实例化
Object rowValue = nestedResultObjects.get(combinedKey);
boolean knownValue = rowValue != null;
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
//通过prefix 把sql的字段映射到实体类里
//重新走getRowValue方法,按ResultMap的resultMapping 对嵌套对象赋值
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;
}
总结
其实这里的源码注释还是比较少的,但是从各方法跟各对象的命名 还是能知道大致意图的。利用debug跟耐心慢慢看,还是能看懂的!