深入了解MyBatis返回值

想了解返回值,我们需要了解resultType,resultMap以及接口方法中定义的返回值。

我们先看resultTyperesultMap

resultType和resultMap

大家应该都知道在MyBatis的<select>标签中有两种设置返回值的方式,分别是resultMapresultType

处理resultMapresultType的代码如下:

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

可以看到这里会优先处理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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上面这段代码中重要的代码如下:

if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) {foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

if中判断的是当前是否支持自动映射(可以配置),这一点很重要,如果不支持,那么没法使用resultType方式,必须用resultMap方式,如果支持,resultType方式和resultMap方式可以同时使用。

这里的基本逻辑是先对没有resultMap的属性自动映射赋值,通过applyAutomaticMappings实现。

如果对象有resultMap,那么还会进行applyPropertyMappings方法。

也就是先处理resultType中自动映射的字段,在处理resultMap中的配置的字段,两者可以同时使用!

下面按照顺序分别说两种方式。

resultType方式

如果支持自动映射,那么会执行applyAutomaticMappings,这里面有metaObject参数。

final MetaObject metaObject = configuration.newMetaObject(resultObject);
  • 1
  • 1

我们看看创建metaObject最关键的一个地方,在Reflector类中:

for (String propName : readablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

这里将实体中的属性名,做了一个映射,是大写的对应实际的属性名。例如ID:id

applyAutomaticMappings中的第一行,首先获取没有映射的列名:

final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
  • 1
  • 1

获取列名的时候:

for (String columnName : columnNames) {final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);if (mappedColumns.contains(upperColumnName)) {mappedColumnNames.add(upperColumnName);} else {unmappedColumnNames.add(columnName);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意这里将列名转换为大写形式,同时保存了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));}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里也转换为了大写。

到这里关于resultTyptresultMap就结束了,但是有一个简单的问题,很多人不懂,是什么?看下个标题。

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看到查询结果有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());
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

可以看到,这些条件完全就是通过方法的返回值决定的。所以如果你写的返回值是数组或者集合,返回的结果就是多个。

如果返回值本身有多个,但是返回值写了一个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;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注意看:

List<T> list = this.<T>selectList(statement, parameter);
  • 1
  • 1

实际上,不管查询一个还是多个结果,MyBatis都是先按多个结果进行查询。拿到list结果后在判断。

如果是查询一个结果的情况,那么list最多只能有一个返回值。通过上面代码的if else if esle可以很明白的理解。

resultTyp,resultMap和返回值多少有关系吗?

没有任何关系。

通过前面resultTyperesultMap的内容,我们应该知道,这个属性是配置JDBC查询结果如何映射到一个对象上的。

不管返回值是什么或者是几个,都是按照resultTyperesultMap生成返回结果。

返回结果的类型由resultTyperesultMap决定。

返回结果的类型

返回结果的类型由resultTyperesultMap决定,是不是很诧异???

实际上就是这种情况。。

举个例子,有个实体CountryCountry2

接口中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

为什么会这样呢?

这是因为接口调用方式是对命名空间方式调用的封装。

当你通过命名空间方式调用的时候,返回结果的类型是什么?

就是由resultTyperesultMap决定的类型,这很容易理解。但是换成接口就觉得不一样了。

这是由于接口方法方式多了返回值,所以我们会认为返回的一定是这个类型。实际上是错的。

特殊情况

当使用纯注解方式时,接口的返回值类型可以起到作用,如果没有使用@ResultType注解指定返回值类型,那么就会使用这里写的返回值类型作为resultType

from: http://blog.csdn.net/isea533/article/details/46442067

深入了解MyBatis返回值相关推荐

  1. mybatis返回值多个对象

    SpringBoot+mysql+Mybatis返回值对象中含有对象集合 1.对象personalHistoryInfo ​ public class PersonalHistoryInfo {/** ...

  2. Mybatis返回值

    默认配置情况: 默认情况下,mybatis 的 update 操作返回值是记录的 matched 的条数,并不是影响的记录条数. insert和delete是受影响的条数 int flag;//roo ...

  3. 2.7 Mybatis——返回值处理

    目录 1.返回值为自定义对象类型 2. 返回值为自定义对象的一个属性 3.返回值为自定义对象多个属性的值 方法一:使用HashMap接收 方法二:使用对象接收 方法三:使用DTO设计模式接收 拓展:D ...

  4. mysql mybatis 返回值类型_使用MyBatis时接收值和返回值选择Map类型或者实体类型

    MyBatis作为现近JavaEE企业级项目开发中常用的持久层框架之一,以其简洁高效的ORM映射和高度的SQL的自由性被广大开发人员认可.Mybatis在接收系统传来的参数和返回的参数时主要可以有Ma ...

  5. Mybatis返回值类型为Map时,空值给过滤掉了

    真恶心,什么问题都遇上了,寻思着终于拿到数据了,一看,数据还不全. 解决方法: 在mybatis.xml配置文件中加上:<setting name="callSettersOnNull ...

  6. 关于mybatis返回值使用包装类型还是基本类型

    有时候,sql需要返回一些数值型的数据,包装类型与基本类型如何选择 字段对应的类型要使用包装类型,不要使用基本类型 因为,如果字段为null的话,拆箱会报空指针异常 聚合函数统计数目的可以使用基本类型 ...

  7. MyBatis更新语句返回值

    mybatis返回值为匹配的行数,若想改为受影响行数加jdbc.url后加上 &useAffectedRows=true

  8. mybatis delete返回值_从零开始学习在IntelliJ IDEA 中使用mybatis

    纯新手,打算学习下mybatis的用法,在官网和教程上看了资料,整理下笔记. 既然是从零开始,就是不依赖任何框架和模板,从空白项目开始.在IDEA上先新建一个空的java项目. 1. 添加项目依赖 需 ...

  9. MyBatis查询结果resultType返回值类型详细介绍

    一.返回一般数据类型 比如要根据 id 属性获得数据库中的某个字段值. mapper 接口: // 根据 id 获得数据库中的 username 字段的值String getEmpNameById(I ...

最新文章

  1. linux下SVN不允许空白日志提交
  2. mysql五表查询_5、MySQL多表查询
  3. Windows 技术篇-LDSGameMaster文件夹有什么用,删除方法
  4. dummy.php 下载,internal dummy connection
  5. 树和二叉树的基本概念(二级)
  6. 程序员如何跟领导提离职_如何优雅地跟老板提加薪?按照这3个步骤来,也不是什么难事...
  7. 一代神机落幕!苹果把iPhone 6 Plus列为过时产品,网友吵翻了...
  8. oracle获取去年年份_「实战」中文检错纠错之语料获取与处理
  9. Spring中HibernateCallback的用法(转)
  10. [洛谷P2370]yyy2015c01的U盘
  11. 经过几天的努力,出了 2 本保姆级编程电子书!
  12. ElasticJob‐Lite:事件追踪
  13. python播放全网视频+打包成exe
  14. 《剑来》语句摘录(七)
  15. Visio绘制维恩图举例
  16. 深度学习、机器学习毕业设计 - 选题建议
  17. 在线教育直播系统 一对一在线直播平台解决方案
  18. 【转载收藏】针对VNPY的软件bugs的修改总结
  19. Android 性能优化五大误区和两大疑点!
  20. android 非SDK API blacklist处理

热门文章

  1. Linux服务器集群系统(一)--转
  2. keepalive学习之软件设计
  3. 奥巴马女儿要上哈佛了!从小给女儿定下了五条规矩! 2017-08-07 07:35 哈佛/美国 转载授权请回复“转载“ 文:益美传媒|编辑:Angela 奥巴马曾说自己最骄傲的一件事,就是即使在长
  4. php manual 反射,Laravel框架源码解析之反射的使用详解
  5. mysql的主从项目经验_mysql5.5主从经验分享
  6. Spring Cloud Alibaba - 17 Nacos Config 配置中心 应用篇
  7. Spring-国际化信息03-容器级的国际化信息资源
  8. Spring-Spring MVC + Spring JDBC + Spring Transaction + Maven 构建web登录模块
  9. Linux - How to Extend/Reduce LVM’s (Logical Volume Management) in Linux
  10. HTML内嵌式CSS背景图填充满无截断重复