sql查詢返回值使用map封裝多個(gè)key和value實(shí)例
直接上代碼,代碼是測(cè)試過(guò)的
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實(shí)現(xiàn)
@Autowired
private SqlSessionFactory sqlSessionFactory;
private static final String mapperPath = "mapper的路徑.";
Map<String, Object> parameter = new HashMap<>();
//設(shè)置參數(shù)
parameter.put("query", query);
//mapper的方法名
String methodName = "selectMedicineSurvey";
//查詢數(shù)據(jù)使用Map封裝
Map<String, BigDecimal> medicineSurveyMap = getStringBigDecimalMap(mapperPath, parameter, methodName);
//查詢數(shù)據(jù)使用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);
//關(guān)流
sqlSession.close();
//獲取結(jié)果
return (Map<String, BigDecimal>) handler.getMappedResults();
}
sqlSession.close();
一定要記得數(shù)據(jù)庫(kù)關(guān)流,不然連接數(shù)就會(huì)把數(shù)據(jù)庫(kù)給卡死
MyBatis查詢兩個(gè)字段,返回Map,一個(gè)字段作為key,一個(gè)字段作為value的實(shí)現(xiàn)
1. 問(wèn)題描述
在使用MyBatis,我們經(jīng)常會(huì)遇到這種情況:SELECT兩個(gè)字段,需要返回一個(gè)Map,其中第一個(gè)字段作為key,第二個(gè)字段作為value。MyBatis的MapKey雖然很實(shí)用,但并不能解決這種場(chǎng)景。這里,就介紹一種使用攔截器來(lái)解決這個(gè)問(wèn)題的方案。
2. 解決方案
源碼詳見(jiàn):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;
/**
* 將查詢結(jié)果映射成map的注解,其中第一個(gè)字段為key,第二個(gè)字段為value.
* <p>
* 注:返回類型必須為{@link java.util.Map Map<K, V>}。K/V的類型通過(guò)MyBatis的TypeHander進(jìn)行類型轉(zhuǎn)換,如有必要可自定義TypeHander。
*
* @author yunjie.du
* @date 2016/12/22 18:44
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface MapF2F {
/**
* 是否允許key重復(fù)。如果不允許,而實(shí)際結(jié)果出現(xiàn)了重復(fù),會(huì)拋出org.springframework.dao.DuplicateKeyException。
*
* @return
*/
boolean isAllowKeyRepeat() default true;
/**
* 對(duì)于相同的key,是否允許value不同(在允許key重復(fù)的前提下)。如果允許,則按查詢結(jié)果,后面的覆蓋前面的;如果不允許,則會(huì)拋出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(), ".");// 當(dāng)前類
String currentMethodName = StringUtils.substringAfterLast(mappedStatement.getId(), ".");// 當(dāng)前方法
Method currentMethod = findMethod(className, currentMethodName);// 獲取當(dāng)前Method
if (currentMethod == null || currentMethod.getAnnotation(MapF2F.class) == null) {// 如果當(dāng)前Method沒(méi)有注解MapF2F
return invocation.proceed();
}
// 如果有MapF2F注解,則這里對(duì)結(jié)果進(jìn)行攔截并轉(zhuǎn)換
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的注冊(cè)器
return result2Map(statement, typeHandlerRegistry, kvTypePair, mapF2FAnnotation);
}
@Override
public Object plugin(Object obj) {
return Plugin.wrap(obj, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 找到與指定函數(shù)名匹配的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;
}
/**
* 獲取函數(shù)返回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);
}
/**
* 將查詢結(jié)果映射成Map,其中第一個(gè)字段作為key,第二個(gè)字段作為value.
*
* @param statement
* @param typeHandlerRegistry MyBatis里typeHandler的注冊(cè)器,方便轉(zhuǎn)換成用戶指定的結(jié)果類型
* @param kvTypePair 函數(shù)指定返回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重復(fù)
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;
}
/**
* 結(jié)果類型轉(zhuǎn)換。
* <p>
* 這里借用注冊(cè)在MyBatis的typeHander(包括自定義的),方便進(jìn)行類型轉(zhuǎn)換。
*
* @param resultSet
* @param columnIndex 字段下標(biāo),從1開(kāi)始
* @param typeHandlerRegistry MyBatis里typeHandler的注冊(cè)器,方便轉(zhuǎn)換成用戶指定的結(jié)果類型
* @param javaType 要轉(zhuǎn)換的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);
/**
* 分離最后一個(gè)代理的目標(biāo)對(duì)象
*
* @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 簡(jiǎn)例
/**
* 批量獲取用戶姓名
*
* @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>
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
約定優(yōu)于配置_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
以前做項(xiàng)目,總是寫Ant配置文件,滿足于自己更靈活的配置,而沒(méi)有去思考,這么做到底值不值得2017-08-08
SpringBoot整合EasyExcel實(shí)現(xiàn)大規(guī)模數(shù)據(jù)的并行導(dǎo)出與壓縮下載
在 Spring Boot 應(yīng)用中,整合 EasyExcel 實(shí)現(xiàn)并行導(dǎo)出數(shù)據(jù)并進(jìn)行 Zip 壓縮下載可以極大地提高數(shù)據(jù)處理效率和用戶體驗(yàn),文中通過(guò)代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-10-10
Spring?Kafka中如何通過(guò)參數(shù)配置解決超時(shí)問(wèn)題詳解
這篇文章主要給大家介紹了關(guān)于Spring?Kafka中如何通過(guò)參數(shù)配置解決超時(shí)問(wèn)題的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01
Spring中自定義數(shù)據(jù)類型轉(zhuǎn)換的方法詳解
Spring3引入了一個(gè)core.onvert包,提供一個(gè)通用類型轉(zhuǎn)換系統(tǒng)。在Spring容器中,可以使用這個(gè)系統(tǒng)作為PropertyEditor實(shí)現(xiàn)的替代,將外部化的bean屬性值字符串轉(zhuǎn)換為所需的屬性類型。本文將詳解這一系統(tǒng)的使用方法,需要的可以參考一下2022-06-06
PowerJob的OmsLogHandler工作流程源碼解析
這篇文章主要為大家介紹了PowerJob的OmsLogHandler工作流程源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
基于java下載中g(shù)etContentLength()一直為-1的一些思路
下面小編就為大家?guī)?lái)一篇基于java下載中g(shù)etContentLength()一直為-1的一些思路。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06

