经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » SQL语言 » 查看文章
sql查询返回值使用map封装多个key和value实例
来源:jb51  时间:2021/7/19 11:49:40  对本文有异议

直接上代码,代码是测试过的

1.重写ResultHandler

  1. public class MapResultHandler implements ResultHandler {
  2. private final Map mappedResults = new HashMap();
  3. @Override
  4. public void handleResult(ResultContext context) {
  5. @SuppressWarnings("rawtypes")
  6. Map map = (Map) context.getResultObject();
  7. mappedResults.put(map.get("key"), map.get("value"));
  8. }
  9. public Map getMappedResults() {
  10. return mappedResults;
  11. }
  12. }

2.在mapper封装

  1. <resultMap id="retMap" type="java.util.HashMap">
  2. <result column="keyName" property="key" javaType="java.lang.String"/>
  3. <result column="val" property="value" javaType="java.math.BigDecimal"/>
  4. </resultMap>

例子:

  1. SELECT F_NAME keyName,nvl(sum (F_METADATA_VALUE),0) val from 表名
  2. GROUP BY F_CODE, F_NAME

3.service实现

  1. @Autowired
  2. private SqlSessionFactory sqlSessionFactory;
  3. private static final String mapperPath = "mapper的路径.";
  4. Map<String, Object> parameter = new HashMap<>();
  5. //设置参数
  6. parameter.put("query", query);
  7. //mapper的方法名
  8. String methodName = "selectMedicineSurvey";
  9. //查询数据使用Map封装
  10. Map<String, BigDecimal> medicineSurveyMap = getStringBigDecimalMap(mapperPath, parameter, methodName);
  11. //查询数据使用Map封装
  12. private Map<String, BigDecimal> getStringBigDecimalMap(String mapperPath, Map<String, Object> parameter, String methodName) {
  13. SqlSession sqlSession = sqlSessionFactory.openSession(true);
  14. MapResultHandler handler = new MapResultHandler();
  15. sqlSession.select(mapperPath + methodName, parameter, handler);
  16. //关流
  17. sqlSession.close();
  18. //获取结果
  19. return (Map<String, BigDecimal>) handler.getMappedResults();
  20. }
  1. sqlSession.close();

一定要记得数据库关流,不然连接数就会把数据库给卡死

MyBatis查询两个字段,返回Map,一个字段作为key,一个字段作为value的实现

1. 问题描述

在使用MyBatis,我们经常会遇到这种情况:SELECT两个字段,需要返回一个Map,其中第一个字段作为key,第二个字段作为value。MyBatis的MapKey虽然很实用,但并不能解决这种场景。这里,就介绍一种使用拦截器来解决这个问题的方案。

2. 解决方案

源码详见:spring-mybatis-test

2.1 注解

  1. package com.adu.spring_test.mybatis.annotations;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. /**
  8. * 将查询结果映射成map的注解,其中第一个字段为key,第二个字段为value.
  9. * <p>
  10. * 注:返回类型必须为{@link java.util.Map Map<K, V>}。K/V的类型通过MyBatis的TypeHander进行类型转换,如有必要可自定义TypeHander。
  11. *
  12. * @author yunjie.du
  13. * @date 2016/12/22 18:44
  14. */
  15. @Documented
  16. @Retention(RetentionPolicy.RUNTIME)
  17. @Target({ ElementType.METHOD })
  18. public @interface MapF2F {
  19. /**
  20. * 是否允许key重复。如果不允许,而实际结果出现了重复,会抛出org.springframework.dao.DuplicateKeyException。
  21. *
  22. * @return
  23. */
  24. boolean isAllowKeyRepeat() default true;
  25. /**
  26. * 对于相同的key,是否允许value不同(在允许key重复的前提下)。如果允许,则按查询结果,后面的覆盖前面的;如果不允许,则会抛出org.springframework.dao.DuplicateKeyException。
  27. *
  28. * @return
  29. */
  30. boolean isAllowValueDifferentWithSameKey() default false;
  31. }

2.2 拦截器

  1. package com.adu.spring_test.mybatis.interceptor;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.ParameterizedType;
  4. import java.lang.reflect.Type;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. import java.sql.Statement;
  8. import java.util.ArrayList;
  9. import java.util.HashMap;
  10. import java.util.List;
  11. import java.util.Map;
  12. import java.util.Objects;
  13. import java.util.Properties;
  14. import org.apache.commons.lang3.StringUtils;
  15. import org.apache.ibatis.executor.resultset.ResultSetHandler;
  16. import org.apache.ibatis.mapping.MappedStatement;
  17. import org.apache.ibatis.plugin.Interceptor;
  18. import org.apache.ibatis.plugin.Intercepts;
  19. import org.apache.ibatis.plugin.Invocation;
  20. import org.apache.ibatis.plugin.Plugin;
  21. import org.apache.ibatis.plugin.Signature;
  22. import org.apache.ibatis.reflection.MetaObject;
  23. import org.apache.ibatis.type.TypeHandler;
  24. import org.apache.ibatis.type.TypeHandlerRegistry;
  25. import org.slf4j.Logger;
  26. import org.slf4j.LoggerFactory;
  27. import org.springframework.dao.DuplicateKeyException;
  28. import com.adu.spring_test.mybatis.annotations.MapF2F;
  29. import com.adu.spring_test.mybatis.util.ReflectUtil;
  30. import javafx.util.Pair;
  31. /**
  32. * MapF2F的拦截器
  33. *
  34. * @author yunjie.du
  35. * @date 2016/12/22 18:44
  36. */
  37. @Intercepts(@Signature(method = "handleResultSets", type = ResultSetHandler.class, args = { Statement.class }))
  38. public class MapF2FInterceptor implements Interceptor {
  39. private Logger logger = LoggerFactory.getLogger(MapF2FInterceptor.class);
  40. @Override
  41. public Object intercept(Invocation invocation) throws Throwable {
  42. MetaObject metaStatementHandler = ReflectUtil.getRealTarget(invocation);
  43. MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("mappedStatement");
  44. String className = StringUtils.substringBeforeLast(mappedStatement.getId(), ".");// 当前类
  45. String currentMethodName = StringUtils.substringAfterLast(mappedStatement.getId(), ".");// 当前方法
  46. Method currentMethod = findMethod(className, currentMethodName);// 获取当前Method
  47. if (currentMethod == null || currentMethod.getAnnotation(MapF2F.class) == null) {// 如果当前Method没有注解MapF2F
  48. return invocation.proceed();
  49. }
  50. // 如果有MapF2F注解,则这里对结果进行拦截并转换
  51. MapF2F mapF2FAnnotation = currentMethod.getAnnotation(MapF2F.class);
  52. Statement statement = (Statement) invocation.getArgs()[0];
  53. Pair<Class<?>, Class<?>> kvTypePair = getKVTypeOfReturnMap(currentMethod);// 获取返回Map里key-value的类型
  54. TypeHandlerRegistry typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();// 获取各种TypeHander的注册器
  55. return result2Map(statement, typeHandlerRegistry, kvTypePair, mapF2FAnnotation);
  56. }
  57. @Override
  58. public Object plugin(Object obj) {
  59. return Plugin.wrap(obj, this);
  60. }
  61. @Override
  62. public void setProperties(Properties properties) {
  63. }
  64. /**
  65. * 找到与指定函数名匹配的Method。
  66. *
  67. * @param className
  68. * @param targetMethodName
  69. * @return
  70. * @throws Throwable
  71. */
  72. private Method findMethod(String className, String targetMethodName) throws Throwable {
  73. Method[] methods = Class.forName(className).getDeclaredMethods();// 该类所有声明的方法
  74. if (methods == null) {
  75. return null;
  76. }
  77. for (Method method : methods) {
  78. if (StringUtils.equals(method.getName(), targetMethodName)) {
  79. return method;
  80. }
  81. }
  82. return null;
  83. }
  84. /**
  85. * 获取函数返回Map中key-value的类型
  86. *
  87. * @param mapF2FMethod
  88. * @return left为key的类型,right为value的类型
  89. */
  90. private Pair<Class<?>, Class<?>> getKVTypeOfReturnMap(Method mapF2FMethod) {
  91. Type returnType = mapF2FMethod.getGenericReturnType();
  92. if (returnType instanceof ParameterizedType) {
  93. ParameterizedType parameterizedType = (ParameterizedType) returnType;
  94. if (!Map.class.equals(parameterizedType.getRawType())) {
  95. throw new RuntimeException(
  96. "[ERROR-MapF2F-return-map-type]使用MapF2F,返回类型必须是java.util.Map类型!!!method=" + mapF2FMethod);
  97. }
  98. return new Pair<>((Class<?>) parameterizedType.getActualTypeArguments()[0],
  99. (Class<?>) parameterizedType.getActualTypeArguments()[1]);
  100. }
  101. return new Pair<>(null, null);
  102. }
  103. /**
  104. * 将查询结果映射成Map,其中第一个字段作为key,第二个字段作为value.
  105. *
  106. * @param statement
  107. * @param typeHandlerRegistry MyBatis里typeHandler的注册器,方便转换成用户指定的结果类型
  108. * @param kvTypePair 函数指定返回Map key-value的类型
  109. * @param mapF2FAnnotation
  110. * @return
  111. * @throws Throwable
  112. */
  113. private Object result2Map(Statement statement, TypeHandlerRegistry typeHandlerRegistry,
  114. Pair<Class<?>, Class<?>> kvTypePair, MapF2F mapF2FAnnotation) throws Throwable {
  115. ResultSet resultSet = statement.getResultSet();
  116. List<Object> res = new ArrayList();
  117. Map<Object, Object> map = new HashMap();
  118. while (resultSet.next()) {
  119. Object key = this.getObject(resultSet, 1, typeHandlerRegistry, kvTypePair.getKey());
  120. Object value = this.getObject(resultSet, 2, typeHandlerRegistry, kvTypePair.getValue());
  121. if (map.containsKey(key)) {// 该key已存在
  122. if (!mapF2FAnnotation.isAllowKeyRepeat()) {// 判断是否允许key重复
  123. throw new DuplicateKeyException("MapF2F duplicated key!key=" + key);
  124. }
  125. Object preValue = map.get(key);
  126. if (!mapF2FAnnotation.isAllowValueDifferentWithSameKey() && !Objects.equals(value, preValue)) {// 判断是否允许value不同
  127. throw new DuplicateKeyException("MapF2F different value with same key!key=" + key + ",value1="
  128. + preValue + ",value2=" + value);
  129. }
  130. }
  131. map.put(key, value);// 第一列作为key,第二列作为value。
  132. }
  133. res.add(map);
  134. return res;
  135. }
  136. /**
  137. * 结果类型转换。
  138. * <p>
  139. * 这里借用注册在MyBatis的typeHander(包括自定义的),方便进行类型转换。
  140. *
  141. * @param resultSet
  142. * @param columnIndex 字段下标,从1开始
  143. * @param typeHandlerRegistry MyBatis里typeHandler的注册器,方便转换成用户指定的结果类型
  144. * @param javaType 要转换的Java类型
  145. * @return
  146. * @throws SQLException
  147. */
  148. private Object getObject(ResultSet resultSet, int columnIndex, TypeHandlerRegistry typeHandlerRegistry,
  149. Class<?> javaType) throws SQLException {
  150. final TypeHandler<?> typeHandler = typeHandlerRegistry.hasTypeHandler(javaType)
  151. ? typeHandlerRegistry.getTypeHandler(javaType) : typeHandlerRegistry.getUnknownTypeHandler();
  152. return typeHandler.getResult(resultSet, columnIndex);
  153. }
  154. }

2.3 ReflectUtil

  1. package com.adu.spring_test.mybatis.util;
  2. import org.apache.ibatis.plugin.Invocation;
  3. import org.apache.ibatis.reflection.MetaObject;
  4. import org.apache.ibatis.reflection.SystemMetaObject;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. /**
  8. * 反射工具类
  9. */
  10. public class ReflectUtil {
  11. private static final Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
  12. /**
  13. * 分离最后一个代理的目标对象
  14. *
  15. * @param invocation
  16. * @return
  17. */
  18. public static MetaObject getRealTarget(Invocation invocation) {
  19. MetaObject metaStatementHandler = SystemMetaObject.forObject(invocation.getTarget());
  20. while (metaStatementHandler.hasGetter("h")) {
  21. Object object = metaStatementHandler.getValue("h");
  22. metaStatementHandler = SystemMetaObject.forObject(object);
  23. }
  24. while (metaStatementHandler.hasGetter("target")) {
  25. Object object = metaStatementHandler.getValue("target");
  26. metaStatementHandler = SystemMetaObject.forObject(object);
  27. }
  28. return metaStatementHandler;
  29. }
  30. }

2.4 MyBatis Datasource配置拦截器

  1. <!-- session factory -->
  2. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  3. <property name="dataSource" ref="dataSource" />
  4. <property name="configLocation" value="classpath:mybatis/mybatis-data-config.xml" />
  5. <property name="mapperLocations" value="classpath:mapper/**/*.xml" />
  6. <property name="plugins">
  7. <array>
  8. <bean class="com.adu.spring_test.mybatis.interceptor.MapF2FInterceptor"/>
  9. </array>
  10. </property>
  11. </bean>

2.5 简例

  1. /**
  2. * 批量获取用户姓名
  3. *
  4. * @param ids
  5. * @return key为ID,value为username
  6. */
  7. @MapF2F()
  8. Map<Long, String> queryUserNamesByIds(@Param("ids") List<Long> ids);
  1. <select id="queryUserNamesByIds" resultType="map">
  2. SELECT id, user_name
  3. FROM user_info
  4. WHERE id IN
  5. <foreach collection="ids" open="(" close=")" separator="," item="item">
  6. #{item}
  7. </foreach>
  8. </select>

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号