首页 > 作文

浅谈MyBatis中@MapKey的妙用

更新时间:2023-04-04 19:51:10 阅读: 评论:0

目录
mybatis @mapkey的妙用背景实现源码分析思考mybatis @mapkey分析1. mapkey注解有啥功能2. mapkey的源码分析1. mappermethod对mapkey的操作2. defaultmapresulthandler是什么

mybatis @mapkey的妙用

背景

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

实现

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

public interface urdao {    @mapkey("id")  map<long, ur> lectbyidlist(@param("idlist") list<long> idlist);  }

需要注意的是:如果mapper.xml中的lect返回类型是list的元素,上面示例的话,resulttype是ur,因为lectmap查询首先是lectlist,之后才是处理list。

源码分析

package org.apache.ibatis.ssion.defaults;public class defaultsqlssion implements sqlssion { ... ... public <k, v> map<k, v> lectmap(string statement, object parameter, string mapkey, rowbounds rowbounds) {  final list<?> list = lectlist(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> lectedmap = mapresulthandler.getmappedresults();  return lectedmap; }  ... ...}

lectmap方法其实是在lectlist后的进一步处理,通过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<ur> list = urdao.lectbyidlist(arrays.aslist(1,2,3)); map<integer, ur> map = list.stream().collect(collectors.tomap(ur::getid, ur -> ur));

mybatis @mapkey分析

先上例子

 @test  public void testshoulectstudentusingmapperclass(){    //waring 就使用他作为第一个测试类    try (sqlssion ssion = sqlmapper.openssion()) {      studentmapper mapper = ssion.getmapper(studentmapper.class);      system.out.println(mapper.liststudentbyids(new int[]{1,2,3}));    }  }  @mapkey("id")  map<integer,studentdo> liststudentbyids(int[] ids);  <lect id="liststudentbyids" resulttype="java.util.map">    lect  * from t_student    where  id in      <foreach collection="array" open="(" parator="," clo=")" item="item">            #{item}      </foreach>  </lect>

结果

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;      } el if (resolvedreturntype instanceof parameterizedtype) {        this.returntype = (class<?>) ((parameterizedtype) resolvedreturntype).getrawtype();      } el {        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和 resul庸懒散thandler      // 这里会判断@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 <k, v> map<k, v> executeformap(sqlssion sqlssion, object[] args) {    map<k, v> result;    object param = method.convertargstosqlcommandparam(args);    if (method.hasrowbounds()) {      rowbounds rowbounds = method.extractrowbounds(args);      // 将map传递给sqlssion了,那就一直往下走      result = sqlssion.lectmap(command.getname(), param, method.getmapkey(), rowbounds);    } el {      result = sqlssion.lectmap(command.getname(), param, method.getmapkey());    }    return result;  }

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

  @override  public <k, v> map<k, v> lectmap(string statement, object parameter, string mapkey, rowbounds rowbounds) {     //这已经做了查询了    final list<? extends v> list = lectlist(statement, parameter, rowbounds);    final defaultneither和either的区别mapresulthandler<k, v> mapresulthandler = new defaultmapresulthandler<>(mapkey,            configuration.getobjectfactory(), configuration.getobjectwrapperfactory(), configuration.getreflectorfactory());    final defaultresultcontext<v> context = new defaultresultcontext<>();    // 遍历list,利用mapresulthandler处理list    for (v o : list) {      context.nextresultobject(o);      mapresulthandler.handleresult(context);    }    return mapresulthandler.getmappedresu胡子男lts();  }

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

2. defaultmapresulthandler是什么

/** * @author clinton begin */public class defaultmapresulthandler<k, v> implements resulthandler<v> {  private final map<k, v> 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<? extends v> 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<k, v> getmappedresults() {    return mappedresults;  }}

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

问题?

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

不一定,看这个例子,mapkey是一个不存在的属性值,那么在map里面就会存在一个null,这是hashmap决定的。

综述:

在mapkey的使用中,要注意mapkey中value字段的唯一性,否则就会造成key值覆盖的操作。同时也要注意,key要肯定存在,否则结果就是null,(如果有特殊操作的话,就另说)话说回来,这里我觉得应该增加强校验。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持www.887551.com。

本文发布于:2023-04-04 19:51:08,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/6f714bf2441cfaeccf9a7fa79cdba2f3.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:浅谈MyBatis中@MapKey的妙用.doc

本文 PDF 下载地址:浅谈MyBatis中@MapKey的妙用.pdf

标签:是在   是一个   注解   参数
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图