深入了解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是我们配置的映射的列,那是不是我们配置的时候必须大写呢?
人生就是要感受美丽的善良的,丑恶的病态的。