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使用详解的更多相关文章

  1. Mybatis SQL映射文件详解

    Mybatis SQL映射文件详解 mybatis除了有全局配置文件,还有映射文件,在映射文件中可以编写以下的顶级元素标签: cache – 该命名空间的缓存配置. cache-ref – 引用其它命 ...

  2. MyBatis的动态SQL详解

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑,本文详解mybatis的动态sql,需要的朋友可以参考下 MyBatis 的一个强大的特性之一通常是它 ...

  3. Mybatis(三) 映射文件详解

    前面说了全局配置文件中内容的详解,大家应该清楚了,现在来说说这映射文件,这章就对输入映射.输出映射.动态sql这几个知识点进行说明,其中高级映射(一对一,一对多,多对多映射)在下一章进行说明. 一.输 ...

  4. 转载 Spring、Spring MVC、MyBatis整合文件配置详解

    Spring.Spring MVC.MyBatis整合文件配置详解   使用SSM框架做了几个小项目了,感觉还不错是时候总结一下了.先总结一下SSM整合的文件配置.其实具体的用法最好还是看官方文档. ...

  5. 利用Intellij+MAVEN搭建Spring+Mybatis+MySql+SpringMVC项目详解

    http://blog.csdn.net/noaman_wgs/article/details/53893948 利用Intellij+MAVEN搭建Spring+Mybatis+MySql+Spri ...

  6. 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. ...

  7. Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解

    封面:洛小汐 作者:潘潘 做大事和做小事的难度是一样的.两者都会消耗你的时间和精力,所以如果决心做事,就要做大事,要确保你的梦想值得追求,未来的收获可以配得上你的努力. 前言 上一篇文章 <My ...

  8. php中关于引用(&)详解

    php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...

  9. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

随机推荐

  1. Neety的基础使用及说明

    BIO(缺乏弹性伸缩能力,并发量小,容易出现内存溢出,出现宕机 每一个客户端对应一个线程 伪异步IO:创建线程池,由线程池里边的线程负责连接处理,M个个请求进来时,会在线程池创建N个线程.容易出现线程 ...

  2. JS基础知识——变量类型和计算(一)

    JS中使用typeof能得到的哪些类型? 何时使用===何时使用==? JS中有哪些内置函数? JS变量按照存储方式区分为哪些类型,描述其特点? 如何理解JSON? 知识点梳理 一.变量类型: (1) ...

  3. Java String 如何加双引号

    public class Test{ public static void main(String[] args){ String str1 = "\"name\"&qu ...

  4. 《Java基础知识》Java IO流详解

    Java IO概念 1. 用于设备之间的数据传输. 2. Java 将操作数据流的功能封装到了IO包中. 3. 数据流流向分:输入流和输出流,操作对象为文件. 4. 流按照操作数据分:字节流(通用)和 ...

  5. 第二次作业-titanic数据集练习

    一.读入titanic.xlsx文件,按照教材示例步骤,完成数据清洗. titanic数据集包含11个特征,分别是: Survived:0代表死亡,1代表存活Pclass:乘客所持票类,有三种值(1, ...

  6. Python连载58-http协议简介

    一.http协议实战 1.URL(Uniform Resource Located) (1)使用FFTP的URL,例如:ftp://rtfm.mit.edu (2)使用HTTP的URL,例如:http ...

  7. ssm集成(maven)& 分模块开发--详细教程

    1 maven版本的ssm 1.1 最简单的版本步骤: (1) 创建maven web项目 (2) 在pom.xml中导入依赖的jar包 (3) 再写配置文件: web.xml <!DOCTYP ...

  8. ElasticSearch 7.4.2 Root mapping definition has unsupported parameters

    新建索引 PUT people { "settings":{ "number_of_shards":3, "number_of_replicas&qu ...

  9. Linux中防火墙命令

    #启动   systemctl start firewalld #开机启动   systemctl enable firewalld #停止   systemctl stop firewalld #禁 ...

  10. standard_init_linux.go:207: exec user process caused "no such file or directory"

    运行docker容器异常中止,使用docker logs CONTAINER_ID查看异常信息如下:standard_init_linux.go:207: exec user process caus ...