MyBatis中@MapKey使用详解
MyBatis中@MapKey使用详解
我们在上一篇文章中讲到在Select返回类型中是返回Map时,是对方法中是否存在注解@MapKey,这个注解我也是第一次看到,当时我也以为是纯粹的返回单个数据对象的Map类型,但是发现还是有些不同的,这个可以用来返回多条记录,具体用法与分析如下。
@MapKey用法
我查了一下MapKey的用法,这里加上MapKey注解后,还有指定一个字段作为返回Map中的key,这里一般也就是使用唯一键来做key,我这就使用id做key吧。
在UserMapper中添加一个根据address查询的方法,方便返回多条数据,UserMapper在Mybatis源码解析之配置加载(一)中有,这里就不再完全展示了,添加的方法如下:
@MapKey("id")
@ResultMap("BaseResultMap")
@Select("select * from user where hotel_address = #{address};")
Map<Long, User> getUserByAddress(@Param("address") String address);
我定义的返回类型为Map<Long, user>,这里id做key,user对象为value,但是要注意的就是User对象中有hotelAddress字段,如果就只加@MapKey注解多半难以映射user对象中的hotelAddress字段,这里加上ResultMap注解试试,不行再想别的办法。
测试用例如下:
Map<Long, User> userMap = userMapper.getUserByAddress("beijing");
for (Map.Entry<Long, User> entry : userMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
执行程序,倒是如之前想的一样,结果如下图:
hotelAddress字段值正常显示出来了,可以把@ResultMap注解去掉试试,结果如下图:
hotelAddress字段显示为null。
这里就不再过多的演示各种用法,这里返回User对象可行,返回Map同样可行,下面开始就开始具体分析@MapKey的使用源码。
2. 源码分析
此处还是要回到Select查询处,如下:
case SELECT:
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 if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
进入到第三种情况executeForMap方法中。
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
Map<K, V> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else {
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
}
return result;
}
继续转入selectMap方法中,如上次所知,这个方法最终调用的仍然是selectList方法,但是我们要搞清楚@MapKey发生作用的位置与原理,在这里要提一句的是,这里向下传输的method.getMapKey()就是我们@MapKey注解中填的value,也就是id。
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
final List<? extends V> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
final DefaultResultContext<V> context = new DefaultResultContext<V>();
for (V o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
return mapResultHandler.getMappedResults();
}
我们在调试代码时可知list这里已经是user对象了。
显而易见的是对查询结果的处理已经在selectList(statement, parameter, rowBounds)方法中了,这里原本想把@ResultMap也一起拿出来说一下,然后发现@ResultMap应该从头开始讲起,所以这个就留到下次再说吧。
从上面代码块中中知MapKey生效处应该是nextResultObject与handleResult方法中,我们先看nextResultObject做的事情。
public void nextResultObject(T resultObject) {
resultCount++;
this.resultObject = resultObject;
}
做了一个类似于初始化的工作,那么重点就是在于handleResult方法中了,转到handleResult方法中。
@Override
public void handleResult(ResultContext<? extends V> context) {
final V value = context.getResultObject();
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
// TODO is that assignment always true?
final K key = (K) mo.getValue(mapKey);
mappedResults.put(key, value);
}
这里的value对象类型为User对象,MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory)这句应该是将user对象转成MetaObject对象,然后通过mapKey取出对应属性的值。
final K key = (K) mo.getValue(mapKey)
可以进getValue看看,到底是如何渠道id字段对应的值。
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else {
return objectWrapper.get(prop);
}
}
@Override
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
return getBeanProperty(prop, object);
}
}
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
return method.invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t);
}
}
这里通过获取到id对应的方法getId,然后反射拿到id对应的值,这里的判断还真多。
拿到id值以后就比较好办了,直接将key和value保存进map中。
final K key = (K) mo.getValue(mapKey);
mappedResults.put(key, value);
然后在selectMap方法中进行返回MapResultSet操作。
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
....
for (V o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
return mapResultHandler.getMappedResults();
}
从而我们得到Map形式的返回结果。
@MapKey作用位置以及Select中executeMap方法就分析到这了。
————————————————
版权声明:本文为CSDN博主「叶长风」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012734441/article/details/85861337
MyBatis中@MapKey使用详解的更多相关文章
- Mybatis SQL映射文件详解
Mybatis SQL映射文件详解 mybatis除了有全局配置文件,还有映射文件,在映射文件中可以编写以下的顶级元素标签: cache – 该命名空间的缓存配置. cache-ref – 引用其它命 ...
- MyBatis的动态SQL详解
MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑,本文详解mybatis的动态sql,需要的朋友可以参考下 MyBatis 的一个强大的特性之一通常是它 ...
- Mybatis(三) 映射文件详解
前面说了全局配置文件中内容的详解,大家应该清楚了,现在来说说这映射文件,这章就对输入映射.输出映射.动态sql这几个知识点进行说明,其中高级映射(一对一,一对多,多对多映射)在下一章进行说明. 一.输 ...
- 转载 Spring、Spring MVC、MyBatis整合文件配置详解
Spring.Spring MVC.MyBatis整合文件配置详解 使用SSM框架做了几个小项目了,感觉还不错是时候总结一下了.先总结一下SSM整合的文件配置.其实具体的用法最好还是看官方文档. ...
- 利用Intellij+MAVEN搭建Spring+Mybatis+MySql+SpringMVC项目详解
http://blog.csdn.net/noaman_wgs/article/details/53893948 利用Intellij+MAVEN搭建Spring+Mybatis+MySql+Spri ...
- idea spring+springmvc+mybatis环境配置整合详解
idea spring+springmvc+mybatis环境配置整合详解 1.配置整合前所需准备的环境: 1.1:jdk1.8 1.2:idea2017.1.5 1.3:Maven 3.5.2 2. ...
- Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解
封面:洛小汐 作者:潘潘 做大事和做小事的难度是一样的.两者都会消耗你的时间和精力,所以如果决心做事,就要做大事,要确保你的梦想值得追求,未来的收获可以配得上你的努力. 前言 上一篇文章 <My ...
- php中关于引用(&)详解
php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
随机推荐
- Eclipse添加spring-tool-suite插件
Eclipse添加spring-tool-suite插件 步骤 1.help --> Eclipse Marketplace,在search框中搜索spring-tool-suite,点击右下角 ...
- 大数据学习笔记——HDFS写入过程源码分析(2)
HDFS写入过程注释解读 & 源码分析 此篇博客承接上一篇未讲完的内容,将会着重分析一下在Namenode获取到元数据后,具体是如何向datanode节点写入真实的数据的 1. 框架图展示 在 ...
- 深入探索Java设计模式之单例模式
单例模式可确保在给定的时间实例中只能创建一个具有全局访问点的对象.这是面向对象编程中最常用的技术之一.尽管它很简单,但从类设计的角度来看可能是最简单的,但是在尝试实现它们之前,必须先解决一些细微的问题 ...
- Linux-部署-Django
Linux-部署-Django-项目过程与问题总结 优才网 2017-04-12 18:00 本篇主要用于记录部署 Django 项目所有踩过的坑. 最近学习 Django 框架开发,将项目部署到 ...
- 【IntelliJ Idea】常用快捷键
[IntelliJ Idea]常用快捷键 转载:https://www.cnblogs.com/yangchongxing/p/10654018.html ============= 调试 ===== ...
- Linux 怎么清理缓存
linux清理缓存的命令 查看缓存的命令 free -m 清理缓存的命令 echo 1 > /proc/sys/vm/drop_caches echo 2 > /proc/sys/v ...
- 在.Net Core中记录日志
一个完善的系统,必然会有非常完善的日志记录,用户的操作.系统的运行状况等信息被完整的记录下来,方便我们对系统进行维护和改进..net core 也为日志记录提供了内置的支持. 在控制台程序中记录日志 ...
- JS 正则表达式^$详解,脱字符^与美元符$同时写表示什么意思?
壹 ❀ 引 对于初学正则的同学来说,^$这两个看似简单的字符却在使用中总让匹配结果超出我们的预期,^什么时候表示行首什么时候表示反义?^ $两个一起写表示什么含义?今天我们就来详细聊聊这两个字符. ...
- C# 只读模式读取txt文件内容
读取txt文件时,提示异常: 文件“..\Log\all_info.txt”正由另一进程使用,因此该进程无法访问此文件 原因: 日志文件通过lognet生成的日志文件(C#使用log4net记录日志) ...
- leaflet-webpack 入门开发系列六矢量瓦片(附源码下载)
前言 leaflet-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载地址 w ...