深入了解MyBatis返回值
深入了解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决定的类型,这非常easy理解。可是换成接口就觉得不一样了。
这是由于接口方法方式多了返回值,所以我们会觉得返回的一定是这个类型。
实际上是错的。
特殊情况
当使用纯注解方式时,接口的返回值类型能够起到作用,假设没有使用@ResultType注解指定返回值类型。那么就会使用这里写的返回值类型作为resultType。
深入了解MyBatis返回值的更多相关文章
- 关于mybatis返回值resultType为空的问题
假设数据库中一个user表 此时只有id为1的数据,当我们查询id为2的年龄时的时候返回值为null 但是在mybatis中预定义UserMapper.xml中 <select id=" ...
- postgresql+mybatis返回值是数据库字段名
mybatis 返回map的时候是下划线 role_id, user_id 两种解决方法 1.重命名 postgresql不支持驼峰 加上双引号重命名 SELECT role_id "ro ...
- mybatis 返回值
转载: 在使用ibatis插入数据进数据库的时候,会用到一些sequence的数据,有些情况下,在插入完成之后还需要将sequence的值返回,然后才能进行下一步的操作. 使用ibatis的 ...
- Mybatis 返回值 返回Map的为空的值
第一种.springMVC和boot通用配置:(Mybatis.xml) <?xml version="1.0" encoding="UTF-8"?> ...
- Mybatis返回值类型是hashmap,输入键值对为空时,key 丢失的问题
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC ...
- mybatis 返回值类型是Map
<select id="selectByMemberKey" resultType="java.util.HashMap"> SELECT memb ...
- Mybatis(三)返回值
Mybatis返回值 MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返回类型的,而resultMap则 ...
- ResultMap和ResultType在使用中的区别、MyBatis中Mapper的返回值类型
在使用mybatis进行数据库连接操作时对于SQL语句返回结果的处理通常有两种方式,一种就是resultType另一种就是resultMap,下面说下我对这两者的认识和理解 resultType:当使 ...
- Mybatis select返回值为map时,选取表字段的两列作为key,value
项目需要从ibatis升级到MyBatis,dao中有一个方法返回Map类型,具体是查询语句查询两个字段,将结果列表字段A的值作为key字段B的值作为value存入Map中作为结果返回: ibatis ...
随机推荐
- Linux系列教程(十九)——Linux文件系统管理之手工分区
上篇博客我们首先介绍了硬盘为什么要分区,以及Linux系统的几种分区类型,然后介绍了Linux系统几个常用的文件系统命令,最后讲解了挂载命令,并通过实例演示了如何挂载光盘和U盘. 本篇博客我们将介绍l ...
- Node.js初探之POST方式传输
小知识:POST比GET传输的数据量大很多 POST发数据--"分段" 实例: 准备一个form.html文件: <!DOCTYPE html> <html> ...
- 编程语言 : Java的动态Web解决方案泛谈
文章概述 最近发现很久前一股脑地学习框架,发觉越发迷糊.知道了框架只是暂时的,重点是基础的技术.该文大篇幅回顾Servlet技术栈和简要的MVC框架. 至于为什么学J2EE,额,大家都用框架,可框架也 ...
- 四、VueJs 填坑日记之搭建Axios接口请求工具
上一章,我们认识了项目的目录结构,以及对项目的目录结构做了一些调整,已经能把项目重新跑起来了.今天我们来搭建api接口调用工具Axios.Vue本身是不支持ajax调用的,如果你需要这些功能就需要安装 ...
- 程序、计算机程序、java初论
一.程序? 程序一词来自生活,通常指完成某些事情的一种既定方式和过程,可以将程序看成对一系列动作的执行过程的描述. 例如:个人去银行取钱 1.带上存折/银行卡去银行 2.取号排队 3.将存折或储蓄卡递 ...
- linux使用yum安装mariadb
一,安装 yum install mariadb mariadb-server 二,如何设置密码 用root 进入mysql后 mysql>set password =password('你的 ...
- SQL SERVER 2012 SEQUENCE
一.Sequence简介 Sequence对象对于Oracle用户来说是最熟悉不过的数据库对象了, 在SQL SERVER2012终于也可以看到这个对象了.Sequence是SQL Server201 ...
- canvas动画之动态绘出六边形
先上 demo: http://en.jsrun.net/W5iKp/show 这两天我一直在研究这个动画,花了大量的时间来想是如何实现的, 一开始我是想在进入 canvas 时按时间来用 lineT ...
- python语言 第一个程序
print("hello word!") # for i in range(1, 10):# for j in range(1, 10):# print(j, "x&qu ...
- 企业级监控zabbix基础
一个标准的监控系统所具备的基本功能: 1.数据的采集 2.为了展示其长期走势,将数据存储下来 3.万一某次采样的结果不在被认为是合理的范围内,然后就会做出告警操作,尽早的让相关人员得知到此消息 4.展 ...