MyBatis查询两个字段,返回Map,一个字段作为key,一个字段作为value的实现
1. 问题描述
在使用MyBatis,我们经常会遇到这种情况:SELECT两个字段,需要返回一个Map,其中第一个字段作为key,第二个字段作为value。MyBatis的MapKey虽然很实用,但并不能解决这种场景。这里,就介绍一种使用拦截器来解决这个问题的方案。
2. 解决方案
源码详见:spring-mybatis-test
2.1 注解
package com.adu.spring_test.mybatis.annotations;import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** 将查询结果映射成map的注解,其中第一个字段为key,第二个字段为value.* <p>* 注:返回类型必须为{@link java.util.Map Map<K, V>}。K/V的类型通过MyBatis的TypeHander进行类型转换,如有必要可自定义TypeHander。** @author yunjie.du* @date 2016/12/22 18:44*/ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MapF2F {/*** 是否允许key重复。如果不允许,而实际结果出现了重复,会抛出org.springframework.dao.DuplicateKeyException。* * @return*/boolean isAllowKeyRepeat() default true;/*** 对于相同的key,是否允许value不同(在允许key重复的前提下)。如果允许,则按查询结果,后面的覆盖前面的;如果不允许,则会抛出org.springframework.dao.DuplicateKeyException。* * @return*/boolean isAllowValueDifferentWithSameKey() default false; }
2.2 拦截器
package com.adu.spring_test.mybatis.interceptor;import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties;import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DuplicateKeyException;import com.adu.spring_test.mybatis.annotations.MapF2F; import com.adu.spring_test.mybatis.util.ReflectUtil;import javafx.util.Pair;/*** MapF2F的拦截器* * @author yunjie.du* @date 2016/12/22 18:44*/ @Intercepts(@Signature(method = "handleResultSets", type = ResultSetHandler.class, args = { Statement.class })) public class MapF2FInterceptor implements Interceptor {private Logger logger = LoggerFactory.getLogger(MapF2FInterceptor.class);@Overridepublic Object intercept(Invocation invocation) throws Throwable {MetaObject metaStatementHandler = ReflectUtil.getRealTarget(invocation);MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("mappedStatement");String className = StringUtils.substringBeforeLast(mappedStatement.getId(), ".");// 当前类String currentMethodName = StringUtils.substringAfterLast(mappedStatement.getId(), ".");// 当前方法Method currentMethod = findMethod(className, currentMethodName);// 获取当前Methodif (currentMethod == null || currentMethod.getAnnotation(MapF2F.class) == null) {// 如果当前Method没有注解MapF2Freturn invocation.proceed();}// 如果有MapF2F注解,则这里对结果进行拦截并转换MapF2F mapF2FAnnotation = currentMethod.getAnnotation(MapF2F.class);Statement statement = (Statement) invocation.getArgs()[0];Pair<Class<?>, Class<?>> kvTypePair = getKVTypeOfReturnMap(currentMethod);// 获取返回Map里key-value的类型TypeHandlerRegistry typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();// 获取各种TypeHander的注册器return result2Map(statement, typeHandlerRegistry, kvTypePair, mapF2FAnnotation);}@Overridepublic Object plugin(Object obj) {return Plugin.wrap(obj, this);}@Overridepublic void setProperties(Properties properties) {}/*** 找到与指定函数名匹配的Method。** @param className* @param targetMethodName* @return* @throws Throwable*/private Method findMethod(String className, String targetMethodName) throws Throwable {Method[] methods = Class.forName(className).getDeclaredMethods();// 该类所有声明的方法if (methods == null) {return null;}for (Method method : methods) {if (StringUtils.equals(method.getName(), targetMethodName)) {return method;}}return null;}/*** 获取函数返回Map中key-value的类型* * @param mapF2FMethod* @return left为key的类型,right为value的类型*/private Pair<Class<?>, Class<?>> getKVTypeOfReturnMap(Method mapF2FMethod) {Type returnType = mapF2FMethod.getGenericReturnType();if (returnType instanceof ParameterizedType) {ParameterizedType parameterizedType = (ParameterizedType) returnType;if (!Map.class.equals(parameterizedType.getRawType())) {throw new RuntimeException("[ERROR-MapF2F-return-map-type]使用MapF2F,返回类型必须是java.util.Map类型!!!method=" + mapF2FMethod);}return new Pair<>((Class<?>) parameterizedType.getActualTypeArguments()[0],(Class<?>) parameterizedType.getActualTypeArguments()[1]);}return new Pair<>(null, null);}/*** 将查询结果映射成Map,其中第一个字段作为key,第二个字段作为value.* * @param statement* @param typeHandlerRegistry MyBatis里typeHandler的注册器,方便转换成用户指定的结果类型* @param kvTypePair 函数指定返回Map key-value的类型* @param mapF2FAnnotation* @return* @throws Throwable*/private Object result2Map(Statement statement, TypeHandlerRegistry typeHandlerRegistry,Pair<Class<?>, Class<?>> kvTypePair, MapF2F mapF2FAnnotation) throws Throwable {ResultSet resultSet = statement.getResultSet();List<Object> res = new ArrayList();Map<Object, Object> map = new HashMap();while (resultSet.next()) {Object key = this.getObject(resultSet, 1, typeHandlerRegistry, kvTypePair.getKey());Object value = this.getObject(resultSet, 2, typeHandlerRegistry, kvTypePair.getValue());if (map.containsKey(key)) {// 该key已存在if (!mapF2FAnnotation.isAllowKeyRepeat()) {// 判断是否允许key重复throw new DuplicateKeyException("MapF2F duplicated key!key=" + key);}Object preValue = map.get(key);if (!mapF2FAnnotation.isAllowValueDifferentWithSameKey() && !Objects.equals(value, preValue)) {// 判断是否允许value不同throw new DuplicateKeyException("MapF2F different value with same key!key=" + key + ",value1="+ preValue + ",value2=" + value);}}map.put(key, value);// 第一列作为key,第二列作为value。 }res.add(map);return res;}/*** 结果类型转换。* <p>* 这里借用注册在MyBatis的typeHander(包括自定义的),方便进行类型转换。* * @param resultSet* @param columnIndex 字段下标,从1开始* @param typeHandlerRegistry MyBatis里typeHandler的注册器,方便转换成用户指定的结果类型* @param javaType 要转换的Java类型* @return* @throws SQLException*/private Object getObject(ResultSet resultSet, int columnIndex, TypeHandlerRegistry typeHandlerRegistry,Class<?> javaType) throws SQLException {final TypeHandler<?> typeHandler = typeHandlerRegistry.hasTypeHandler(javaType)? typeHandlerRegistry.getTypeHandler(javaType) : typeHandlerRegistry.getUnknownTypeHandler();return typeHandler.getResult(resultSet, columnIndex);}}
2.3 ReflectUtil
package com.adu.spring_test.mybatis.util;import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory;/*** 反射工具类*/ public class ReflectUtil {private static final Logger logger = LoggerFactory.getLogger(ReflectUtil.class);/*** 分离最后一个代理的目标对象* * @param invocation* @return*/public static MetaObject getRealTarget(Invocation invocation) {MetaObject metaStatementHandler = SystemMetaObject.forObject(invocation.getTarget());while (metaStatementHandler.hasGetter("h")) {Object object = metaStatementHandler.getValue("h");metaStatementHandler = SystemMetaObject.forObject(object);}while (metaStatementHandler.hasGetter("target")) {Object object = metaStatementHandler.getValue("target");metaStatementHandler = SystemMetaObject.forObject(object);}return metaStatementHandler;}}
View Code
2.4 MyBatis Datasource配置拦截器
<!-- session factory --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="configLocation" value="classpath:mybatis/mybatis-data-config.xml" /><property name="mapperLocations" value="classpath:mapper/**/*.xml" /><property name="plugins"><array><bean class="com.adu.spring_test.mybatis.interceptor.MapF2FInterceptor"/></array></property></bean>
2.5 简例
/*** 批量获取用户姓名* * @param ids* @return key为ID,value为username*/@MapF2F()Map<Long, String> queryUserNamesByIds(@Param("ids") List<Long> ids);
<select id="queryUserNamesByIds" resultType="map">SELECT id, user_nameFROM user_infoWHERE id IN<foreach collection="ids" open="(" close=")" separator="," item="item">#{item}</foreach> </select>
参考:
- Mybatis返回Map的一种实现
转载于:https://www.cnblogs.com/waterystone/p/6214322.html
MyBatis查询两个字段,返回Map,一个字段作为key,一个字段作为value的实现相关推荐
- 老司机学习MyBatis之如何通过select返回Map
From: https://blog.csdn.net/Gaomb_1990/article/details/80638177 一.案例 当要查询的结果是一个Map的时候,这里分为两种情况: ①返回单 ...
- mybatis支持驼峰自动转换sql吗_mybatis-plus返回map自动转驼峰配置操作
mybatis-plus返回map自动转驼峰配置object-wrapper-factory不生效问题解决:配置map-underscore-to-camel-case: true不生效问题解决 很多 ...
- MySqlClient访问tinyint字段返回布尔值
MySqlClient访问tinyint字段返回布尔值 原文 MySqlClient访问tinyint字段返回布尔值 症状: 使用MySqlClient访问tinyint unsign 字 ...
- MySQL.MyBatis怎么将查询的两个字段作为Map的key和value
MySQL.MyBatis怎么将查询的两个字段作为Map的key和value 问题的由来 前端使用Echarts图标显示汇总数据.需要形式如下的数据: {"C20": 42.01, ...
- 【开源项目笔记:platform-wechat-mall】Mybatis 查询数据库返回部分字段
在本开源项目中数据库操作采用了Mybatis,不美的是查询数据一律返回VO结构,即使只需要其中的一两个字段也如此,更别提多表联合查询时空字段占了90%,需要改进. 本文描述Mybatis如何在单表查询 ...
- mybatis查询返回map的问题
文章目录 背景 1.mybatis只返回单个map 2.查询返回map的list 3.利用mybatis的@MapKey注解返回map 4.重写handler 背景 假设背景: 想获取某个省下各个市有 ...
- mybatis查询返回null的原因_可怕!你没看错,这次确实是纯手工实现一个MyBatis框架...
目录 前言 JDBC MyBatis 源码分析 前置知识 原理分析 自己实现一个 MyBatis 框架 前言 MyBatis是一个非常优秀的持久层应用框架,目前几乎已经一统天下.既然是持久层框架,那么 ...
- map mybatis 的字段返回0_mybatis返回map类型数据空值字段不显示(三种解决方法)
mybatis的配置 mybatis-config.xml 2,springBoot配置 application.properties 添加 #mybatis resultType equal m ...
- mybatis 字段名自动转小写_mybatis 返回Map类型key改为小写的操作
默认情况下,当resultType="java.util.Map"时,返回的key值都是大写的. 现在想key改成自己想要的,只需为查询出来的字段增加个别名即可. 如: selec ...
最新文章
- Oracle PL/SQL的安装
- 3行代码,Python数据预处理提速6倍!(附链接)
- 带负荷测试要求二次最小电流_检修状态下二次带负荷测试方案的优化研究
- 数据库优化的几条基本策略
- 【全真互联网下音视频通信技术演进】
- 我说省略号然后点点点点点点
- 如何下载HLS视频到本地(m3u8)
- 涤纶针织物用分散染料染色时,为什么小样与大样不符?
- Spring velocity 中文乱码 解决方案
- 无源蜂鸣器c语言编程,无源蜂鸣器鸣叫
- 非线性回归(Non-linear Regression)学习笔记
- ORB_SLAM3系统框图
- 4.微信支付之刷卡支付
- 什么是ICP经营许可证?
- win7 win10双系统开机系统引导
- JAVA写100以内的偶数和
- jQJQJQJQJQJQ
- zigbee3.0 BDB 介绍(一)
- OpenCv中Numpy常用函数
- javascript 编码_我们的1,600小时JavaScript编码课程
热门文章
- jQuery 实现 select模糊查询 反射机制
- 如何将CSDN文档转换成IPYNB格式的文档?
- 全国大学生智能汽车竞赛 --智慧物流创意组
- 第十五届全国大学生智能车全国总决赛获奖信息-浙江赛区
- LED,硅光电池的光能-电能转换是可逆的吗?
- AD7705 16-bit Delta-Sigma AD 转换器
- 数字示波器使用中的欠采样
- int的长度_Java中String长度有限制吗?身边的同事每一个人知道!
- applicationcontext添加配置_Spring源码分析2 — spring XML配置文件的解析流程
- php怎样弄成中文,php怎样替换中文字符