MyBatis中@MapKey的妙用

  • 背景

在實際開發中,有一些場景需要我們返回主鍵或者唯一鍵爲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));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章