千家信息网

MyBatis中@MapKey怎么用

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章将为大家详细讲解有关MyBatis中@MapKey怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。MyBatis @MapKey的妙用背景在实际开发中,
千家信息网最后更新 2026年11月02日MyBatis中@MapKey怎么用

这篇文章将为大家详细讲解有关MyBatis中@MapKey怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

MyBatis @MapKey的妙用

背景

在实际开发中,有一些场景需要我们返回主键或者唯一键为Key、Entity为Value的Map集合,如Map,之后我们就可以直接通过map.get(key)的方式来获取Entity。

实现

MyBatis为我们提供了这种实现,Dao示例如下:

public interface UserDao {        @MapKey("id")    Map selectByIdList(@Param("idList") List idList);    }

需要注意的是:如果Mapper.xml中的select返回类型是List的元素,上面示例的话,resultType是User,因为selectMap查询首先是selectList,之后才是处理List。

源码分析

package org.apache.ibatis.session.defaults;public class DefaultSqlSession implements SqlSession {  ... ...  public  Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {    final List list = selectList(statement, parameter, rowBounds);    final DefaultMapResultHandler mapResultHandler = new DefaultMapResultHandler(mapKey,        configuration.getObjectFactory(), configuration.getObjectWrapperFactory());    final DefaultResultContext context = new DefaultResultContext();    for (Object o : list) {      context.nextResultObject(o);      mapResultHandler.handleResult(context);    }    Map selectedMap = mapResultHandler.getMappedResults();    return selectedMap;  }    ... ...}

selectMap方法其实是在selectList后的进一步处理,通过mapKey获取DefaultMapResultHandler类型的结果处理器,然后遍历list,调用handler的handleResult把每个结果处理后放到map中,最后返回map。

package org.apache.ibatis.executor.result;public class DefaultMapResultHandler implements ResultHandler {  private final Map 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 list = userDao.selectByIdList(Arrays.asList(1,2,3));  Map map = list.stream().collect(Collectors.toMap(User::getId, user -> user));

Mybatis @MapKey分析

先上例子

 @Test  public void testShouSelectStudentUsingMapperClass(){    //waring 就使用他作为第一个测试类    try (SqlSession session = sqlMapper.openSession()) {      StudentMapper mapper = session.getMapper(StudentMapper.class);      System.out.println(mapper.listStudentByIds(new int[]{1,2,3}));    }  }  @MapKey("id")  Map listStudentByIds(int[] ids);  

结果

1. MapKey注解有啥功能

Mapkey可以让查询的结果组装成Map,Map的key是@MapKey指定的字段,Value是实体类。如上图所示

2. MapKey的源码分析

还是从源码分析一下他是怎么实现的,要注意MapKey不是写在Xml中的,而是标注在方法上的。所以,对于XML文件来说,他肯定不是在解析文件的时候操作的。对于Mapper注解实现来说,理论上来说是在解析的时候用的,但是对比XML的解析来说,应该不是。多说一点,想想Spring ,开始的时候都是XML,最后采用的注解,并且注解的功能和XML的对应起来,所以在解析XML是怎么解析的,在解析注解的时候就应该是怎么解析的。

还是老套路,点点看看,看看哪里引用到了他,从下图看到,用到的地方一个是MapperMethod,一个是MapperAnnotationBuilder,在MapperAnnotationBuilder里面只是判断了一下,没有啥实质性的操作,这里就不用管。只看前者。

1. MapperMethod对MapKey的操作

看过之前文章的肯定知道,MapperMethod是在哪里创建的。这个是在调用mapper接口的查询的时候创建的。接口方法的执行最终会调用到这个对象的execute方法

MapperMethod里面包含两个对象SqlCommand和MethodSignature,就是在MethodSignature里面引用了MapKey的

 public MethodSignature(Configuration configuration, Class mapperInterface, Method method) {     //判断此方法的返回值的类型      Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);      if (resolvedReturnType instanceof Class) {        this.returnType = (Class) resolvedReturnType;      } else if (resolvedReturnType instanceof ParameterizedType) {        this.returnType = (Class) ((ParameterizedType) resolvedReturnType).getRawType();      } else {        this.returnType = method.getReturnType();      }     // 返回值是否为空      this.returnsVoid = void.class.equals(this.returnType);     // 返回是否是一个列表或者数组      this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();      // 返回值是否返回一个游标      this.returnsCursor = Cursor.class.equals(this.returnType);       // 返回值是否是一个Optional      this.returnsOptional = Optional.class.equals(this.returnType);        // 重点来了,这里会判断返回值是一个MapKey,并且会将MapKey里Value的值赋值给MapKey      this.mapKey = getMapKey(method);        // 返回值是否是一个map      this.returnsMap = this.mapKey != null;      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); //找到方法参数里面 第一个 参数类型为ResultHandler的值      // 这里是处理方法参数里面的param注解, 注意方法参数里面有两个特殊的参数 RowBounds和 ResultHandler      // 这里会判断@param指定的参数,并且会将这些参数组成一个map,key是下标,value是param指定的参数,如果没有,就使用方法参数名      this.paramNameResolver = new ParamNameResolver(configuration, method);    }

上面的代码这次最重要的是mapKey的赋值操作getMapKey,来看看他是什么样子

private String getMapKey(Method method) {      String mapKey = null;      if (Map.class.isAssignableFrom(method.getReturnType())) {        final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);        if (mapKeyAnnotation != null) {          mapKey = mapKeyAnnotation.value();        }      }      return mapKey;    }

上面介绍了MapKey是在哪里解析的,下面分析Mapkey是怎么应用的,抛开所有的不说,围绕查询来说。经过上面的介绍。已经对查询的流程很清晰了,因为查询还是普通的查询,所以,MapKey在组装值的时候才会发送作用,下面就看看吧

还是老套路,既然赋值给MethodSignature的mapKey了,点点看看,哪里引用了他

下面的没有啥可看的,看看上面,在MapperMethod里面用到了,那就看看

//看这个名字就能知道,这是一个执行Map查询的操作private  Map executeForMap(SqlSession sqlSession, Object[] args) {    Map result;    Object param = method.convertArgsToSqlCommandParam(args);    if (method.hasRowBounds()) {      RowBounds rowBounds = method.extractRowBounds(args);      // 将Map传递给sqlSession了,那就一直往下走      result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);    } else {      result = sqlSession.selectMap(command.getName(), param, method.getMapKey());    }    return result;  }

一直点下去,就看到下面的这个了,可以看到,这里将mapKey传递给了DefaultMapResultHandler,对查询的结果进行处理。

  @Override  public  Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {     //这已经做了查询了    final List list = selectList(statement, parameter, rowBounds);    final DefaultMapResultHandler mapResultHandler = new DefaultMapResultHandler<>(mapKey,            configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());    final DefaultResultContext context = new DefaultResultContext<>();    // 遍历list,利用mapResultHandler处理List    for (V o : list) {      context.nextResultObject(o);      mapResultHandler.handleResult(context);    }    return mapResultHandler.getMappedResults();  }

这里很明确了,先做正常的查询,在对查询到的结果做处理(DefaultMapResultHandler)。

2. DefaultMapResultHandler是什么
/** * @author Clinton Begin */public class DefaultMapResultHandler implements ResultHandler {  private final Map mappedResults;  private final String mapKey;  private final ObjectFactory objectFactory;  private final ObjectWrapperFactory objectWrapperFactory;  private final ReflectorFactory reflectorFactory;  @SuppressWarnings("unchecked")  public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {    this.objectFactory = objectFactory;    this.objectWrapperFactory = objectWrapperFactory;    this.reflectorFactory = reflectorFactory;    this.mappedResults = objectFactory.create(Map.class);    this.mapKey = mapKey;  } // 逻辑就是这里,  @Override  public void handleResult(ResultContext context) {    //拿到遍历的list的当前值。    final V value = context.getResultObject();    //构建MetaObject,    final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);    // TODO is that assignment always true?    // 获取mapKey指定的属性,放在mappedResults里面。    final K key = (K) mo.getValue(mapKey);    mappedResults.put(key, value);  } // 返回结果  public Map getMappedResults() {    return mappedResults;  }}

这里的逻辑很清晰,对查询查到的list。做遍历,利用反射获取MapKey指定的字段,并且组成Map,放在一个Map(mappedResults,这默认就是HashMap)里面。

问题?

1, 从结果中获取mapkey字段的操作,这个字段总是有的吗?

不一定,看这个例子,MapKey是一个不存在的属性值,那么在Map里面就会存在一个Null,这是Hashmap决定的。

关于"MyBatis中@MapKey怎么用"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

查询 处理 参数 方法 结果 是在 时候 注解 分析 字段 类型 面的 就是 源码 篇文章 还是 逻辑 两个 例子 功能 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技官网 互联网高科技企业 互联网科技大会 联恩商钥互联网科技有限公司 顺网科技2021年互联网排名 安控科技rtu产品工业互联网 创业天下互联网科技有限公司 欧洲十大科技互联网公司 互联网系的金融科技公司 上海宁丞科技互联网 荆州市昕露互联网络科技有限公司 注册互联网科技公司经营范围大全 徐州市互联网科技有限公司 伊宁互联网科技做什么的 华中科技大学互联网的牛人 高科技互联网套路 2021互联网科技公司排名 张召忠谈中国互联网科技 艺电能源互联网科技有限公司招聘 数字互联网科技有限公司 齐家互联网科技 广州宏创互联网科技 深圳齐家互联网科技 新华互联网科技校企合作 隆华科技工业互联网 互联网教育科技公司vipkid 承德科技产业互联网发展 用友工业互联网科技发展 上海科技产业互联网平台 领航科技大厦互联网公司
0