在实际开发中,有一些场景需要我们返回主键或者唯一键为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));