评论

收藏

[Java] sql查询返回值使用map封装多个key和value实例

编程语言 编程语言 发布于:2021-10-17 20:47 | 阅读数:750 | 评论:0

这篇文章主要介绍了sql查询返回值使用map封装多个key和value实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
直接上代码,代码是测试过的
1.重写ResultHandler
public class MapResultHandler implements ResultHandler {
  private final Map mappedResults = new HashMap();
  @Override
  public void handleResult(ResultContext context) {
    @SuppressWarnings("rawtypes")
    Map map = (Map) context.getResultObject();
    mappedResults.put(map.get("key"), map.get("value"));
  }
  public Map getMappedResults() {
    return mappedResults;
  }
}
2.在mapper封装
<resultMap id="retMap" type="java.util.HashMap">
    <result column="keyName" property="key" javaType="java.lang.String"/>
    <result column="val" property="value" javaType="java.math.BigDecimal"/>
  </resultMap>
例子:
SELECT F_NAME keyName,nvl(sum (F_METADATA_VALUE),0) val from 表名
GROUP BY F_CODE, F_NAME
3.service实现
@Autowired
   private SqlSessionFactory sqlSessionFactory;
   private static final String mapperPath = "mapper的路径.";    
 Map<String, Object> parameter = new HashMap<>();
 //设置参数
     parameter.put("query", query);
 //mapper的方法名
     String methodName = "selectMedicineSurvey";
     //查询数据使用Map封装
     Map<String, BigDecimal> medicineSurveyMap = getStringBigDecimalMap(mapperPath, parameter, methodName);
   //查询数据使用Map封装
   private Map<String, BigDecimal> getStringBigDecimalMap(String mapperPath, Map<String, Object> parameter, String methodName) {
     SqlSession sqlSession = sqlSessionFactory.openSession(true);
     MapResultHandler handler = new MapResultHandler();
     sqlSession.select(mapperPath + methodName, parameter, handler);
    //关流
     sqlSession.close();
    //获取结果
     return (Map<String, BigDecimal>) handler.getMappedResults();
   }
sqlSession.close();
一定要记得数据库关流,不然连接数就会把数据库给卡死
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);
  @Override
  public 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);// 获取当前Method
    if (currentMethod == null || currentMethod.getAnnotation(MapF2F.class) == null) {// 如果当前Method没有注解MapF2F
      return 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);
  }
  @Override
  public Object plugin(Object obj) {
    return Plugin.wrap(obj, this);
  }
  @Override
  public 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;
  }
}
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_name
  FROM user_info
  WHERE id IN
    <foreach collection="ids" open="(" close=")" separator="," item="item">
      #{item}
    </foreach>
</select>
以上为个人经验,希望能给大家一个参考,也希望大家多多支持CodeAE代码之家
原文链接:https://blog.csdn.net/weixin_45245297/article/details/106604533

关注下面的标签,发现更多相似文章