Mybatis @MapKey注解原理详解

后端 潘老师 3个月前 (12-11) 104 ℃ (0) 扫码查看

本文主要讲解关于Mybatis @MapKey注解原理,探究Mybatis将结果映射为Map的秘密相关内容,让我们来一起学习下吧!

在日常使用Mybatis进行开发时,不知道你是否遇到过这样的需求,即需要从一堆数据中找出自己所期待的某个数据。对于这样的需求,大致可以通过如下几种方式来进行处理:

  1. sql语句中拼接where条件,从而达到对数据筛选的目的,进而找出我们所想要的数据;
  2. 查询所有数据,然后通过循环遍历的方式找出自己所想要的那条数据信息
  3. 利用@MapKey注解,将查询到的数据存放在一个Map中,然后通过key进行获取。

其中,方式1和方式2的做法相对来说比较常规,在此便不重点分析了。接下来,我们重点来分析Mybaits内部@MapKey的使用和其背后的工作原理。

前言

MyBatis中,@MapKey 主要用于在映射查询结果到一个Map。换言之,当你执行一个查询并期望返回一个Map时,你可以使用@MapKey来进行结果集的映射。而Mybatis内部会将查询到的结果映射为一个key-value的形式。

接下来,先让我们将通过一个简单的例子来快速了解@MapKey的基本使用。

@MapKey使用示例

假设有一个User类,其有idaganame三个属性。此时,你想根据员工的id来获取整个User对象的映射。那么你的MyBatis映射器接口可以写为如下的形式:

@Mapper
public interface EmployeeMapper {
    @Select("SELECT id, name,age FROM test_user")
    @MapKey("id")
    Map<Integer, User> getAllUser();
}

在上述例子中,当调用getAllUser方法后,将返回一个Map。其中每个员工的id是键,对应的User对象是值,具体执行结果如下所示。

不难发现,通过使用@MapKey你可以有效地将数据库查询的结果直接映射为Java中的Map结构。总之,当查询返回多个对象,并且你希望根据这些对象的某个属性来将它们组织成一个Map时,@MapKey非常有用。

熟悉了@MapKey的时候后,接下来我们再来看一看@MapKey背后的原理。

剖析@MapKey的背后的原理

在分析@MapKey之前,我们不妨先考虑这样一个问题,那就是如果我们要剖析Mybatis内部对于@MapKey的解析,我们应该从何处入手呢?

在专栏文章Mybatis流程分析(五): sql语句与接口中方法绑定的”细节”中,我们曾提到在Mybatis中,sql语句和方法的绑定是通过配置解析器将配置信息解析为MappedStatement来实现的。换言之,方法待执行的sql会存放在MappedStatement之中。 进一步,当我们调用Mapper中的方法时,其最终会执行Mapper接口中方法所绑定的sql

而在这一过程中会依赖一个MapperMethod的对象。正如Mybatis流程分析(六): Mybatis中方法和sql语句的桥梁——MapperProxy中提到的那样,在MyBatis中,MapperMethod 是一个内部类,其用于桥接MyBatisMapper接口和实际执行的SQL命令,这背后的大致逻辑如下:

  • 首先,当你调用一个Mapper接口中的方法时,MyBatis会使用MapperProxy代理这个调用。
  • 其次,MapperProxy在确定出被调用的方法后,会使用MapperMethod来执行相应的数据库操作。
  • 然后,MapperMethod确定方法的参数、SQL语句和返回类型,然后调用MyBatis的执行层来执行SQL语句。
  • 最终,执行完毕后,MapperMethod将结果处理(转换)成适当的返回类型并返回给调用者。

而在MapperMethod确定方法的参数信息时,其又需要借助一个名为MethodSignature的内部类。对于这个类,你只需知道其主要用于封装和处理与 Mapper 接口中定义的方法相关的元数据和操作。

(注:MethodSignature主要负责提取和管理与方法调用相关的信息,如参数、返回类型、是否存在注解等。换言之,你只需知道MethodSignature的主要就是为了记录方法的签名信息即可。)

话说到这个份上,最开始的问题是是不是已经很清晰了?如果我们要剖析@MapKey注解被解析的地方,是不是只要去MethodSignature类寻找就可以了?明白了这点,再来看看MethodSignature中的相关代码才不至于迷惑。

MethodSignature中有关@MapKey注解的代码


public static class MethodSignature {

  // ...省略其他无关代码
  
  // 存放mapKey中配置的key信息
  private final String mapKey;
 
  public MethodSignature() {
    // ...省略其他无关代码
    this.mapKey = getMapKey(method);
  // ...省略其他无关代码
  }

事实上,在MethodSignature中有关@MapKey注解的解析全部委托给方法getMapKey来进行处理。而该方法的大致逻辑就是获取方法的上@MapKey然后将其中的key值信息,记录到mapKey字段中。

知晓了@MapKey的解析后,我们再来看Mybatis内部是如何将查询到结果封装为一个Map

在Mybatis流程分析(九): 从JDBC出发讲透Mybatis结果集的处理逻辑中,我们曾提到过在Mybatis中,结果集的处理全部会委托给handleResult进行处理。而Mybatis内部默认使用的结果处理器为DefaultMapResultHandler。所以接下来,我们进入到DefaultMapResultHandler中一看Mybatis内部对于@MapKey的处理。这一过程调笔者就不带着注意分析了,感兴趣的可自己进行debug。整个过程的调用逻辑如下:

public void handleResult(ResultContext<? extends V> context) {
  final V value = context.getResultObject();
  final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
  // TODO is that assignment always true?
  final K key = (K) mo.getValue(mapKey);
  mappedResults.put(key, value);
}

不难发现,在handleResult方法发现就是将我们制定的key取出为MapKey,然后将数据作为MapValue。其实,@MapKey背后的逻辑还是相对简单的,总结下来无非两步:

  • 注解解析。解析@MapKey中配置的属性字段,存放至变量mapKey中;
  • 结果集处理。循环处理结果集,依据配置好的key将结果集封装为一个map

总结

接下来,我们对Mybatis内部对于@MapKey的解析过程进行一个简单的总结。在Mybatis内部对于@MapKey的处理大致经历了如下过程:

  1. 注解解析:当 MyBatis 解析你的Mapper接口时,它会识别方法上的 @MapKey 注解。这个注解包含一个值,指定了应该用作 Map 键的字段名。
  2. 执行 SQL 查询:当调用标记有 @MapKey 注解的方法时,MyBatis 会执行相应的 SQL 查询。
  3. 结果集映射MyBatis 遍历SQL查询的结果集。对于结果集中的每一行:
    • 它首先创建一个新的对象,该对象的类型是 Mapper 接口方法的返回类型Map的值类型。
    • 然后,MyBatis 将当前行的数据填充到这个新创建的对象中。
  4. 键值对填充MyBatis 使用 @MapKey 注解指定的字段值作为键(Key),将新创建的对象作为值(Value),填充到最终的 Map 结构中。

总的来说@MapKey背后的逻辑还是相对简单的,不算很难,基本都是些Java基础知识的相关应用。希望文章对你有所帮助!

以上就是关于Mybatis @MapKey注解原理,探究Mybatis将结果映射为Map的秘密相关的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/12464.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】