MyBatis中@MapKey的妙用

  • 背景

在实际开发中,有一些场景需要我们返回主键或者唯一键为Key、Entity为Value的Map集合,如Map,之后我们就可以直接通过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));

你可能感兴趣的:(MyBatis)