MyBatis的MapKey注解實(shí)例解析
使用
mybatis中有很多實(shí)用的注解,但是平時想不起來使用。今天就來講一下MapKey是如何使用的
說明:本文基于mybatis原生框架3.3.0-SNAPSHOT
一、數(shù)據(jù)準(zhǔn)備
數(shù)據(jù)庫準(zhǔn)備一張user
表,插入一點(diǎn)測試數(shù)據(jù)
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") List<User> queryAll1(); @MapKey("username") @Select("select * from user limit 1") Map<String, User> queryAll2(); }
這里我們的mapper接口只有兩個方法queryAll1
queryAll2
。這兩個方法執(zhí)行的SQL是一樣的,SQL的含義也一樣,就是從user表中取出一條數(shù)據(jù)。
不同的是
queryAll1
queryAll2
的返回值不一樣queryAll1
沒有使用MapKey注解,返回值是User對象,符合SQL返回的只有一條記錄的語義queryAll2
使用MapKey注解,但是返回值卻是一個Map對象?這似乎不符合SQL返回的語義。SQLselect * from user limit 1
只返回一條記錄。怎么返回一個Map<String, User>
對象呢?這就是MapKey這個注解的特別之處: 它能夠?qū)⒋娣艑ο蟮腖ist轉(zhuǎn)化為 key值為對象的某一屬性的Map。MapKey有一個屬性value,該屬性值填入的就是對象的屬性名,作為Map的key值??床欢@句話沒關(guān)系,看完執(zhí)行結(jié)果回頭再來看就懂了!
三、實(shí)戰(zhàn)
使用mybatis的SqlSession獲取Mapper代理對象,分別執(zhí)行
@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(); Map<String, User> users2 = userMapper.queryAll2(); System.out.println("不使用MapKey查詢: " + users3); System.out.println("使用MapKey查詢: " + users4); }
輸出結(jié)果
不使用MapKey查詢: User{id=1, username='111', password='222', birthday='333'}
使用MapKey查詢: {111=User{id=1, username='111', password='222', birthday='333'}}
可以看到,添加了MapKey注解的方法執(zhí)行結(jié)果Map的key就是注解里value值對應(yīng)的User對象的屬性值。value就是SQL查詢得到的結(jié)果User。
這就是MapKey這個注解的特別之處: 它能夠?qū)⒋娣艑ο蟮腖ist轉(zhuǎn)化為 key值為對象的某一屬性的Map。MapKey有一個屬性value,該屬性值填入的就是對象的屬性名,作為Map的key值
現(xiàn)在再來看這句話,是不是就能理解了?
實(shí)戰(zhàn)2——注意事項
Mapper接口中的方法標(biāo)注了MapKey后,即使SQL返回多條結(jié)果,最終方法返回的結(jié)果只有一條。這是因為user表中的username字段全是一樣的。如果把MapKey注解中的value值改為其他user表中不一樣的字段,返回結(jié)果就會是多條記錄啦
@MapKey("username") @Select("select * from user limit 10") Map<String, User> queryAll5(); @MapKey("id") @Select("select * from user limit 10") Map<String, User> queryAll6();
執(zhí)行方法
Map<String, User> users5 = userMapper.queryAll5(); System.out.println("users5: " + users5); Map<String, User> users6 = userMapper.queryAll6(); System.out.println("users6: " + users6);
輸出結(jié)果
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'}}
如果標(biāo)注了MapKey,則返回結(jié)果Map的value類型不可以是List,否則執(zhí)行方法會報錯。下面是錯誤示例。
@MapKey("username") // 執(zhí)行會報錯 @Select("select * from user limit 10") Map<String, List<User>> queryAll5();
原理
@MapKey("username") @Select("select * from user limit 10") Map<String, User> queryAll5();
我們針對如上這個方法進(jìn)行分析,在執(zhí)行SQL時會調(diào)用SqlSession中的如下代碼
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { //轉(zhuǎn)而去調(diào)用selectList final List<?> list = selectList(statement, parameter, rowBounds); final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey, configuration.getObjectFactory(), configuration.getObjectWrapperFactory()); final DefaultResultContext context = new DefaultResultContext(); for (Object o : list) { //循環(huán)用DefaultMapResultHandler處理每條記錄 context.nextResultObject(o); mapResultHandler.handleResult(context); } //注意這個DefaultMapResultHandler里面存了所有已處理的記錄(內(nèi)部實(shí)現(xiàn)可能就是一個Map),最后再返回一個Map return mapResultHandler.getMappedResults(); }
來分析源碼
- 使用Executor查詢結(jié)果,這里的SQL是
select * from user limit 10
,SQL執(zhí)行的結(jié)果返回給List對象,List中確實(shí)有10條記錄 - 構(gòu)造一個對象
DefaultMapResultHandler mapResultHandler
,它是用來處理結(jié)果集的映射的, - 遍歷第一步中List得到的結(jié)果集對象
調(diào)用mapResultHandler.handleResult(context);
方法將List結(jié)果集中每一條記錄對應(yīng)Mapkey中的屬性值取出,作為Map的key加入到集合里。handleResult源碼如下。其中主要關(guān)注這一行就可以了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轉(zhuǎn)為Map }
通過handleResult方法源碼可以看到,對于List結(jié)果集中的一條記錄,取出屬性username的值作為Map的key值添加到mappedResults集合中。那么如果key值相同就會被覆蓋!這就是實(shí)戰(zhàn)篇坑1的原理
最后是通過mapResultHandler.getMappedResults();
方法返回第4步中的map最為最終方法的返回值。
總結(jié)
MapKey的作用:它能夠?qū)⒋娣艑ο蟮腖ist轉(zhuǎn)化為 key值為對象的某一屬性的Map。MapKey有一個屬性value,該屬性值填入的就是對象的屬性名,作為Map的key值
使用場景:可以針對結(jié)果集中的某個屬性去重,而不在乎其他字段是否重復(fù)
以上就是MyBatis的MapKey注解實(shí)例解析的詳細(xì)內(nèi)容,更多關(guān)于MyBatis MapKey注解的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
深度解析SpringBoot中@Async引起的循環(huán)依賴
本文主要介紹了深度解析SpringBoot中@Async引起的循環(huán)依賴,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02JavaFX實(shí)現(xiàn)拖拽結(jié)點(diǎn)效果
這篇文章主要為大家詳細(xì)介紹了JavaFX實(shí)現(xiàn)拖拽結(jié)點(diǎn)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12idea將maven項目改成Spring boot項目的方法步驟
這篇文章主要介紹了idea將maven項目改成Spring boot項目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java設(shè)計模塊系列之書店管理系統(tǒng)單機(jī)版(一)
這篇文章主要為大家詳細(xì)介紹了Java單機(jī)版的書店管理系統(tǒng)設(shè)計模塊和思想第一章,感興趣的小伙伴們可以參考一下2016-08-08Springboot集成Kafka進(jìn)行批量消費(fèi)及踩坑點(diǎn)
本文主要介紹了Springboot集成Kafka進(jìn)行批量消費(fèi)及踩坑點(diǎn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12深入了解HttpClient的ResponseHandler接口
這篇文章主要為大家介紹了深入了解HttpClient的ResponseHandler接口,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10