Mybatis @MapKey注解返回指定Map源码解析与用例

2023-10-31 01:30

本文主要是介绍Mybatis @MapKey注解返回指定Map源码解析与用例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 前言
    • 技术积累
      • 什么是MyBatis
      • @MapKey注解
    • 用例展示
    • MapKey注解源码解析
    • 写在最后

前言

最近在开发的一个业务功能需要从一批数据中根据业务字段提取数据,对于这个需求可能有的同学就直接用for或者stream循环的方式进行处理了。但是,作为一个资深的搬砖人,秉承着能够框架完成的绝不手写的勤奋思维,我们可以用Mybatis调用数据库查询出数据后直接用@MapKey注解直接封装成以业务字段为key的Map,后续直接根据key进行数据获取。

技术积累

什么是MyBatis

MyBatis就不用多说了,就是一个基于Java语言的持久层框架,它通过XML描述符或注解将对象与存储过程或SQL语句进行映射,并提供了普通SQL查询、存储过程和高级映射等操作方式,使得操作数据库变得非常方便。

@MapKey注解

查看@MapKey注解源码

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MapKey {String value();
}

由以上源码可知,MapKey注解应用于运行时的方法上,也就是我们的DAO的方法上。
用MapKey注解可以直接返回一个以指定字段为key,整体数据为value的Map,通过这个Map我们可以直接获取指定字段的具体数据。

用例展示

1、xml增加查询sql语句

<resultMap id="businessGroupByUserMap" type="com.ysjr.base.domain.entity.vo.roam.RoamBusinessVo"><result column="count" property="count" jdbcType="INTEGER"/><result column="belongUserId" property="belongUserId" jdbcType="BIGINT"/>
</resultMap>
<!--流转根据用户查询商机分组-->
<select id="businessGroupByUser" parameterType="map" resultMap="businessGroupByUserMap">select count(customer_business_id) as count,belong_user_id as belongUserId from t_customer_businesswhere delete_status = 1and belong_company_child_id = #{belongCompanyChildId}and clue_business_type = 2and `status` in ('BS002','BS003','BS004','BS005')group by belong_user_id;
</select>

2、DAO增加调用方法

/*** 流转商机根据用户分组* @param belongCompanyChildId* @author senfel* @date 2023/10/27 9:50* @return*/
@MapKey("belongUserId")
Map<Long, RoamBusinessVo> businessGroupByUser(@Param("belongCompanyChildId") Long belongCompanyChildId);

3、增加测试用例

/*** MapKeyAnnotationTest* @author senfel* @version 1.0* @date 2023/10/27 9:57*/
@Slf4j
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class MapKeyAnnotationTest {@Autowiredprivate CustomerBusinessDao customerBusinessDao;/*** MapKey resultMap* @author senfel* @date 2023/10/27 9:59* @return void*/@Testpublic void resultMap(){Map<Long, RoamBusinessVo>  longIntegerMap = customerBusinessDao.businessGroupByUser(2l);System.err.println(longIntegerMap);}
}

4、debug模式运行测试用例

在这里插入图片描述

由上图所知,我们调用Mybatis执行sql语句后直接返回的就是已经封装为Map的数据,并且Map的key为我们注解中的@MapKey(“belongUserId”),数据为查询出的字段。

得到这个数据结构后,我们直接可以在业务代码获取指定 belongUserId 的数据进行处理,不用再进行循环什么的操作。

当然可能有的同学会问为什么不一次查询一条呢,这个就要根据业务逻辑来了,比如我们这里的根据子公司统计商机数据量并根据所属人分组,还有一些查询数据量大单次查询影响性能的情况,都是需要考虑一次性从数据库拉取多条数据。

MapKey注解源码解析

MapKey源码逻辑比较简单,大致就是如果我们在方法上增加了@MapKey注解,Mybatis框架就会帮助我们根据指定key封装为指定类型的Map数据。

首先我们进入@MapKey源码查看其引用的类:
在这里插入图片描述

进入MapperMethod查看具体执行逻辑,有一个方法签名MethodSignature方法获取了注解中的key字段:

//方法签名,在签名中获取了我们注解中指定的key字段
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);//获取指定key字段this.mapKey = getMapKey(method);this.returnsMap = (this.mapKey != null);this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);this.paramNameResolver = new ParamNameResolver(configuration, method);
}
//直接获取到了我们dao层@MapKey注解写入的value
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;
}

既然获取了注解key字段,我们我们就可以继续查找看什么地方调用了这个 this.mapKey
在这里插入图片描述

如上图所示,我们看到了在Mybatis执行sql语句的时候会将mapKey中key字段传入,我们继续进入查看执行逻辑:

//执行返回map
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {Map<K, V> result;Object param = method.convertArgsToSqlCommandParam(args);if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);} else {result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());}return result;
}

继续往下查看Mybatis如何对结果封装为Map的:
在这里插入图片描述

直接进入默认的方法查看执行逻辑,这里用DefaultMapResultHandler默认的map结果处理器,并循环调用handleResult方法进行数据封装:

//查看封装为map的方法
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {final List<? extends V> list = selectList(statement, parameter, rowBounds);//源码这里用DefaultMapResultHandler默认的map结果处理器final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());final DefaultResultContext<V> context = new DefaultResultContext<V>();//循环数据库返回的对象for (V o : list) {context.nextResultObject(o);mapResultHandler.handleResult(context);}return mapResultHandler.getMappedResults();
}

我们直接进入DefaultMapResultHandler默认的map结果处理器,查看handleResult方法发现就是将我们制定的key取出为Map的Key,然后将数据作为Map的Value:

@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) {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);
}

写在最后

Mybatis框架是Java后端常用的持久层框架,其中的@MapKey注解可以直接返回指定类型的Map。其核心原理就是对数据集合循环处理将指定字段作为key,数据作为value直接返回一个Map让我们直接使用以完成特定的业务功能。

⭐️路漫漫其修远兮,吾将上下而求索 🔍

这篇关于Mybatis @MapKey注解返回指定Map源码解析与用例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/311876

相关文章

解析 XML 和 INI

XML 1.TinyXML库 TinyXML是一个C++的XML解析库  使用介绍: https://www.cnblogs.com/mythou/archive/2011/11/27/2265169.html    使用的时候,只要把 tinyxml.h、tinystr.h、tinystr.cpp、tinyxml.cpp、tinyxmlerror.cpp、tinyxmlparser.

持久层 技术选型如何决策?JPA,Hibernate,ibatis(mybatis)

转自:http://t.51jdy.cn/thread-259-1-1.html 持久层 是一个项目 后台 最重要的部分。他直接 决定了 数据读写的性能,业务编写的复杂度,数据结构(对象结构)等问题。 因此 架构师在考虑 使用那个持久层框架的时候 要考虑清楚。 选择的 标准: 1,项目的场景。 2,团队的技能掌握情况。 3,开发周期(开发效率)。 传统的 业务系统,通常业

20.Spring5注解介绍

1.配置组件 Configure Components 注解名称说明@Configuration把一个类作为一个loC容 器 ,它的某个方法头上如果注册7@Bean , 就会作为这个Spring容器中的Bean@ComponentScan在配置类上添加@ComponentScan注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的 <context:component-scan>@Sc

SpringBoot集成Netty,Handler中@Autowired注解为空

最近建了个技术交流群,然后好多小伙伴都问关于Netty的问题,尤其今天的问题最特殊,功能大概是要在Netty接收消息时把数据写入数据库,那个小伙伴用的是 Spring Boot + MyBatis + Netty,所以就碰到了Handler中@Autowired注解为空的问题 参考了一些大神的博文,Spring Boot非controller使用@Autowired注解注入为null的问题,得到

springboot家政服务管理平台 LW +PPT+源码+讲解

3系统的可行性研究及需求分析 3.1可行性研究 3.1.1技术可行性分析 经过大学四年的学习,已经掌握了JAVA、Mysql数据库等方面的编程技巧和方法,对于这些技术该有的软硬件配置也是齐全的,能够满足开发的需要。 本家政服务管理平台采用的是Mysql作为数据库,可以绝对地保证用户数据的安全;可以与Mysql数据库进行无缝连接。 所以,家政服务管理平台在技术上是可以实施的。 3.1

Java注解详细总结

什么是注解?         Java注解是代码中的特殊标记,比如@Override、@Test等,作用是:让其他程序根据注解信息决定怎么执行该程序。         注解不光可以用在方法上,还可以用在类上、变量上、构造器上等位置。 自定义注解  现在我们自定义一个MyTest注解 public @interface MyTest{String aaa();boolean bbb()

SQL Server中,用Restore DataBase把数据库还原到指定的路径

restore database 数据库名 from disk='备份文件路径' with move '数据库文件名' to '数据库文件放置路径', move '日志文件名' to '日志文件存放置路径' Go 如: restore database EaseWe from disk='H:\EaseWe.bak' with move 'Ease

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载

tf.split()函数解析

API原型(TensorFlow 1.8.0): tf.split(     value,     num_or_size_splits,     axis=0,     num=None,     name='split' ) 这个函数是用来切割张量的。输入切割的张量和参数,返回切割的结果。  value传入的就是需要切割的张量。  这个函数有两种切割的方式: 以三个维度的张量为例,比如说一

基于Java医院药品交易系统详细设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W+,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码+数据库🌟 感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人  Java精品实战案例《600套》 2023-2025年最值得选择的Java毕业设计选题大全:1000个热