深入了解MyBatis返回值
想了解返回值,我们需要了解resultType,resultMap以及接口方法中定义的返回值。
我们先看resultType和resultMap
resultType和resultMap大家应该都知道在MyBatis的<select>标签中有两种设置返回值的方式,分别是resultMap和resultType。
处理resultMap和resultType的代码如下:
( String resultMap, Class<?> resultType, ResultSetType resultSetType, MappedStatement.Builder statementBuilder) { resultMap = applyCurrentNamespace(resultMap, true); List<ResultMap> resultMaps = new ArrayList<ResultMap>(); if (resultMap != null) { String[] resultMapNames = resultMap.split(","); for (String resultMapName : resultMapNames) { try { resultMaps.add(configuration.getResultMap(resultMapName.trim())); } catch (IllegalArgumentException e) { throw new IncompleteElementException("Could not find result map " + resultMapName, e); } } } else if (resultType != null) { ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder( configuration, statementBuilder.id() + "-Inline", resultType, new ArrayList<ResultMapping>(), null); resultMaps.add(inlineResultMapBuilder.build()); } statementBuilder.resultMaps(resultMaps); statementBuilder.resultSetType(resultSetType); }可以看到这里会优先处理resultMap,但是也使用了resultType。
接下来看MyBatis获取数据后,如果处理一行结果(以简单数据为例,不考虑嵌套情况):
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().size() > 0; if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) { 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; }上面这段代码中重要的代码如下:
if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) { foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;if中判断的是当前是否支持自动映射(可以配置),这一点很重要,如果不支持,那么没法使用resultType方式,必须用resultMap方式,如果支持,resultType方式和resultMap方式可以同时使用。
这里的基本逻辑是先对没有resultMap的属性自动映射赋值,通过applyAutomaticMappings实现。
如果对象有resultMap,那么还会进行applyPropertyMappings方法。
也就是先处理resultType中自动映射的字段,在处理resultMap中的配置的字段,两者可以同时使用!
下面按照顺序分别说两种方式。
resultType方式如果支持自动映射,那么会执行applyAutomaticMappings,这里面有metaObject参数。
final MetaObject metaObject = configuration.newMetaObject(resultObject);我们看看创建metaObject最关键的一个地方,在Reflector类中:
for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); }这里将实体中的属性名,做了一个映射,是大写的对应实际的属性名。例如ID:id。
在applyAutomaticMappings中的第一行,首先获取没有映射的列名:
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);获取列名的时候:
for (String columnName : columnNames) { final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH); if (mappedColumns.contains(upperColumnName)) { mappedColumnNames.add(upperColumnName); } else { unmappedColumnNames.add(columnName); } }注意这里将列名转换为大写形式,同时保存了mappedColumnNames映射的列和unmappedColumnNames未映射的列。
因为不管是属性名还是查询列都是大写的,所以只要列名和属性名大写一致,就会匹配上。
因此我们在写sql的时候,不需要对查询列的大小写进行转换,自动匹配是不区分大小写的。
resultMap方式这种方式也很简单,上面提到了mappedColumnNames,在判断是否为映射列的时候,使用mappedColumns.contains(upperColumnName)进行判断,mappedColumns是我们配置的映射的列,那是不是我们配置的时候必须大写呢?