深入了解MyBatis返回值
2015-06-10 17:07
363 查看
深入了解MyBatis返回值
想了解返回值,我们需要了解resultType,
resultMap以及接口方法中定义的返回值。
我们先看
resultType和
resultMap
resultType和resultMap
大家应该都知道在MyBatis的<select>标签中有两种设置返回值的方式,分别是
resultMap和
resultType。
处理
resultMap和
resultType的代码如下:
private void setStatementResultMap( 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是我们配置的映射的列,那是不是我们配置的时候必须大写呢?
实际上不用,这里也不区分大小写,在
<result column="xxx" ../>的
column也不区分大小写,看下面的代码:
for (ResultMapping compositeResultMapping : resultMapping.getComposites()) { final String compositeColumn = compositeResultMapping.getColumn(); if (compositeColumn != null) { resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH)); } }
这里也转换为了大写。
到这里关于
resultTypt和
resultMap就结束了,但是有一个简单的问题,很多人不懂,是什么?看下个标题。
MyBatis接口返回值
接口返回值通常是一个结果,或者是List和数组。
MyBatis如何知道我想要返回一个结果还是多个结果?
在MapperMethod中的部分代码如下:
if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); }
可以看到查询结果有4中情况,
void,
list(和
array),
map,
one。
这里重要就是if的判断条件,这种判断条件计算方法:
this.returnType = method.getReturnType(); this.returnsVoid = void.class.equals(this.returnType); this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
可以看到,这些条件完全就是通过方法的返回值决定的。所以如果你写的返回值是数组或者集合,返回的结果就是多个。
如果返回值本身有多个,但是返回值写了一个POJO,不是集合或者数组时会怎样?
答案是会报错
TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size())。
不管是返回一个结果还是多个结果,MyBatis都是安装多个结果进行查询,
selectOne是查询一个,
selectList是查询多个,我们看看
selectOne代码:
public <T> T selectOne(String statement, Object parameter) { List<T> list = this.<T>selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; } }
注意看:
List<T> list = this.<T>selectList(statement, parameter);
实际上,不管查询一个还是多个结果,MyBatis都是先按多个结果进行查询。拿到
list结果后在判断。
如果是查询一个结果的情况,那么
list最多只能有一个返回值。通过上面代码的
if else if esle可以很明白的理解。
resultTyp
,resultMap
和返回值多少有关系吗?
没有任何关系。通过前面
resultType和
resultMap的内容,我们应该知道,这个属性是配置JDBC查询结果如何映射到一个对象上的。
不管返回值是什么或者是几个,都是按照
resultType和
resultMap生成返回结果。
返回结果的类型由
resultType和
resultMap决定。
返回结果的类型
返回结果的类型由resultType和
resultMap决定,是不是很诧异???
实际上就是这种情况。。
举个例子,有个实体
Country和
Country2。
接口中
List<Country> selectAll(),xml中的
<select id="selectAll" resultType="Country2">.
当你通过接口调用的时候,返回值是什么?你以为自己的
List中的对象类型是
Country,但他们实际上都是
Country2
如果接口方法为
Country selectById(Integer id),xml中为
<select id="selectById" resultType="Country2">,由于类型不一致,查询的时候才会报错:
java.lang.ClassCastException: xx.Country2 cannot be cast to xx.Country
为什么会这样呢?
这是因为接口调用方式是对命名空间方式调用的封装。当你通过命名空间方式调用的时候,返回结果的类型是什么?
就是由
resultType和
resultMap决定的类型,这很容易理解。但是换成接口就觉得不一样了。
这是由于接口方法方式多了返回值,所以我们会认为返回的一定是这个类型。实际上是错的。
特殊情况
当使用纯注解方式时,接口的返回值类型可以起到作用,如果没有使用@ResultType注解指定返回值类型,那么就会使用这里写的返回值类型作为
resultType。
相关文章推荐
- [android]Xutils详细介绍
- 安装配置PhoneGap开发环境(一)
- pthread_mutex_init & 互斥锁pthread_mutex_t的使用
- nodejs shell
- Android中级教程(二)之调用另一个Activity---Intent对象的使用!
- POJ 2182
- POJ 2181
- SQLServer学习笔记系列4
- SQLServer学习笔记系列3
- Modbus中CRC校验
- sd卡详细资料
- leetcode 25 -- Reverse Nodes in k-Group
- js 闭包 详解
- POJ 2153
- POJ 2159
- SQLServer学习笔记系列2
- java.lang.ClassNotFoundException: com.sun.faces.config.ConfigureListener
- C# 操作Word文档(转)
- Median of Two Sorted Arrays(沒完成別看)
- Socket通信