- 背景
在實際開發中,有一些場景需要我們返回主鍵或者唯一鍵爲Key、Entity爲Value的Map集合,如Map<Long, User>
,之後我們就可以直接通過map.get(key)
的方式來獲取Entity。
- 實現
MyBatis爲我們提供了這種實現,Dao示例如下:
public interface UserDao {
@MapKey("id")
Map<Long, User> selectByIdList(@Param("idList") List<Long> idList);
}
需要注意的是:如果Mapper.xml中的select返回類型是List的元素,上面示例的話,resultType是User,因爲selectMap查詢首先是selectList,之後纔是處理List。
- 源碼分析
package org.apache.ibatis.session.defaults;
public class DefaultSqlSession implements SqlSession {
... ...
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
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) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
Map<K, V> selectedMap = mapResultHandler.getMappedResults();
return selectedMap;
}
... ...
}
selectMap方法其實是在selectList後的進一步處理,通過mapKey獲取DefaultMapResultHandler類型的結果處理器,然後遍歷list,調用handler的handleResult把每個結果處理後放到map中,最後返回map。
package org.apache.ibatis.executor.result;
public class DefaultMapResultHandler<K, V> implements ResultHandler {
private final Map<K, V> mappedResults;
... ...
public void handleResult(ResultContext context) {
// TODO is that assignment always true?
final V value = (V) context.getResultObject();
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory);
// TODO is that assignment always true?
final K key = (K) mo.getValue(mapKey);
mappedResults.put(key, value);
}
... ...
}
可以看出DefaultMapResultHandler是通過mapKey從元數據中獲取K,然後mappedResults.put(key, value)
放到map中。
- 思考
@MapKey這種處理是在查詢完後做的處理,實際上我們也可以自己寫邏輯將List轉成Map,一個Lambda表達式搞定,如下:
List<User> list = userDao.selectByIdList(Arrays.asList(1,2,3));
Map<Integer, User> map = list.stream().collect(Collectors.toMap(User::getId, user -> user));