世界速看:MyBatis的MapKey注解实例解析
目录
使用一、数据准备二、Mapper配置UserMapper接口三、实战实战2——注意事项原理总结使用
mybatis中有很多实用的注解,但是平时想不起来使用。今天就来讲一下MapKey是如何使用的
说明:本文基于mybatis原生框架3.3.0-SNAPSHOT
一、数据准备
数据库准备一张user
表,插入一点测试数据
(资料图片)
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `password` varchar(20) DEFAULT NULL, `age` int(11) DEFAULT NULL, `birthday` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1018 DEFAULT CHARSET=utf8mb4
select * from user;
二、Mapper配置
UserMapper接口
public interface UserMapper { @Select("select * from user limit 1") ListqueryAll1(); @MapKey("username") @Select("select * from user limit 1") Map queryAll2(); }
这里我们的mapper接口只有两个方法queryAll1
queryAll2
。这两个方法执行的SQL是一样的,SQL的含义也一样,就是从user表中取出一条数据。
不同的是
queryAll1
queryAll2
的返回值不一样queryAll1
没有使用MapKey注解,返回值是User对象,符合SQL返回的只有一条记录的语义queryAll2
使用MapKey注解,但是返回值却是一个Map对象?这似乎不符合SQL返回的语义。SQLselect * from user limit 1
只返回一条记录。怎么返回一个Map
对象呢?这就是MapKey这个注解的特别之处: 它能够将存放对象的List转化为 key值为对象的某一属性的Map。MapKey有一个属性value,该属性值填入的就是对象的属性名,作为Map的key值。看不懂这句话没关系,看完执行结果回头再来看就懂了!
三、实战
使用mybatis的SqlSession获取Mapper代理对象,分别执行
@org.junit.Test public void testMapKey() throws IOException { InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = factory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User users1 = userMapper.queryAll1(); Mapusers2 = userMapper.queryAll2(); System.out.println("不使用MapKey查询: " + users3); System.out.println("使用MapKey查询: " + users4); }
输出结果
不使用MapKey查询: User{id=1, username="111", password="222", birthday="333"}
使用MapKey查询: {111=User{id=1, username="111", password="222", birthday="333"}}
可以看到,添加了MapKey注解的方法执行结果Map的key就是注解里value值对应的User对象的属性值。value就是SQL查询得到的结果User。
这就是MapKey这个注解的特别之处: 它能够将存放对象的List转化为 key值为对象的某一属性的Map。MapKey有一个属性value,该属性值填入的就是对象的属性名,作为Map的key值
现在再来看这句话,是不是就能理解了?
实战2——注意事项
Mapper接口中的方法标注了MapKey后,即使SQL返回多条结果,最终方法返回的结果只有一条。这是因为user表中的username字段全是一样的。如果把MapKey注解中的value值改为其他user表中不一样的字段,返回结果就会是多条记录啦
@MapKey("username") @Select("select * from user limit 10") MapqueryAll5(); @MapKey("id") @Select("select * from user limit 10") Map queryAll6();
执行方法
Mapusers5 = userMapper.queryAll5(); System.out.println("users5: " + users5); Map users6 = userMapper.queryAll6(); System.out.println("users6: " + users6);
输出结果
users5: {111=User{id=11, username="111", password="222", birthday="333"}}
users6: {1=User{id=1, username="111", password="222", birthday="333"},
3=User{id=3, username="111", password="222", birthday="333"},
4=User{id=4, username="111", password="222", birthday="333"},
5=User{id=5, username="111", password="222", birthday="333"},
6=User{id=6, username="111", password="222", birthday="333"},
7=User{id=7, username="111", password="222", birthday="333"},
8=User{id=8, username="111", password="222", birthday="333"},
9=User{id=9, username="111", password="222", birthday="333"},
10=User{id=10, username="111", password="222", birthday="333"},
11=User{id=11, username="111", password="222", birthday="333"}}
如果标注了MapKey,则返回结果Map的value类型不可以是List,否则执行方法会报错。下面是错误示例。
@MapKey("username") // 执行会报错 @Select("select * from user limit 10") Map> queryAll5();
原理
@MapKey("username") @Select("select * from user limit 10") MapqueryAll5();
我们针对如上这个方法进行分析,在执行SQL时会调用SqlSession中的如下代码
publicMap selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { //转而去调用selectList final List> list = selectList(statement, parameter, rowBounds); final DefaultMapResultHandler mapResultHandler = new DefaultMapResultHandler (mapKey, configuration.getObjectFactory(), configuration.getObjectWrapperFactory()); final DefaultResultContext context = new DefaultResultContext(); for (Object o : list) { //循环用DefaultMapResultHandler处理每条记录 context.nextResultObject(o); mapResultHandler.handleResult(context); } //注意这个DefaultMapResultHandler里面存了所有已处理的记录(内部实现可能就是一个Map),最后再返回一个Map return mapResultHandler.getMappedResults(); }
来分析源码
使用Executor查询结果,这里的SQL是select * from user limit 10
,SQL执行的结果返回给List对象,List中确实有10条记录构造一个对象DefaultMapResultHandler mapResultHandler
,它是用来处理结果集的映射的,遍历第一步中List得到的结果集对象
调用mapResultHandler.handleResult(context);
方法将List结果集中每一条记录对应Mapkey中的属性值取出,作为Map的key加入到集合里。handleResult源码如下。其中主要关注这一行就可以了mappedResults.put(key, value);
。
@Override public void handleResult(ResultContext context) { // TODO is that assignment always true? //得到一条记录 //这边黄色警告没法去掉了?因为返回Object型 final V value = (V) context.getResultObject(); //MetaObject.forObject,包装一下记录 //MetaObject是用反射来包装各种类型 final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory); final K key = (K) mo.getValue(mapKey); mappedResults.put(key, value); //这个类主要目的是把得到的List转为Map }
通过handleResult方法源码可以看到,对于List结果集中的一条记录,取出属性username的值作为Map的key值添加到mappedResults集合中。那么如果key值相同就会被覆盖!这就是实战篇坑1的原理
最后是通过mapResultHandler.getMappedResults();
方法返回第4步中的map最为最终方法的返回值。
总结
MapKey的作用:它能够将存放对象的List转化为 key值为对象的某一属性的Map。MapKey有一个属性value,该属性值填入的就是对象的属性名,作为Map的key值
使用场景:可以针对结果集中的某个属性去重,而不在乎其他字段是否重复
以上就是MyBatis的MapKey注解实例解析的详细内容,更多关于MyBatis MapKey注解的资料请关注脚本之家其它相关文章!
相关新闻
- 世界速看:MyBatis的MapKey注解实例解析
- IPO周报|上半年打新26股首日浮盈破万,本周9股申购 全球讯息
- 央视《潜伏者》首播将至!6大实力派联手,这回柳云龙有对手了_全球看点
- 贵州“村超”带火“超经济”
- Shopee推出新卖家十“全”十“免”计划
- 四川盆地黄淮等地暴雨连连 华北高温起起伏伏
- 再度开启高温模式——这一阵子北方为何热过南方_天天百事通
- AI巨头积极布局数字版权:持续关注投资契机
- 节水手抄报资料 内容_节水手抄报内容资料 新视野
- 今日快讯:“执法犬队”事件系10岁熊孩子所为 具体是什么情况?
- 【世界新要闻】港股异动 | 科济药业-B(02171)涨超4% 与罗氏就治疗胃癌药物开展临床试验
- 【世界热闻】快收藏!7月最新乒乓球安排出炉,国乒至少参加2站,日本奥运选拔
- 当前速看:最大赢家!杨舒予夺得亚洲杯冠军,媒体晒荣誉:奥运奖牌在列
- 天天速讯:班集体的发展阶段(班集体的概念)
- 【腐烂】02 世界新动态
- 天天微速讯:新能源下乡专属车型!奇瑞QQ冰淇淋雪糕款2.99万“清新”上市
- 天天最资讯丨陈文:福州山地救援“吹哨人”
- 斯基拉:伊卡尔迪收到沙特3年共7500万欧报价,但首选留在欧洲_环球快播
- 2023年7月1日江苏省二甘醇价格最新行情预测
- 逍遥情缘手游新手必看攻略大全,装备提升攻略技巧