Mybatis 返回Map类型结果集
程序员文章站
2022-07-01 07:52:15
...
1、key ==> Object(非基础类型),Mybatis自带功能
mapper.xml 配置 resultType="java.util.Map"
mapper.java 配置 @MapKey("key") // "key"表示需要当key的字段名
mapper.java 配置 Map<Stirng, Object> methodName()
或
mapper.java 配置 @MapKey("key") // "key"表示需要当key的字段名
mapper.xml 配置 resultType="com.clyy.entity.User"
mapper.java 配置 Map<Stirng, User> methodName()
注意:此处的Object 一般为java.util.Map,当配置为Map时需要注意Map的value.class 是Object,就算代码定义为String,也不一定会转换为String,实际类型会根据数据库的字段值类型来确定
2、key ==> value(基础类型),自定义拦截器
自定义拦截器资料:http://www.cnblogs.com/waterystone/p/6214322.html
拦截器原理资料:http://www.cnblogs.com/fangjian0423/p/mybatis-interceptor.html
注意:此拦截器默认查询数据中的第一个字段为Key,第二个字段为Value。可配置@MapToKeyValue来定义keyColumnName、valueColumnName的字段名
// 自定义注解 @MapToKeyValue
/**
* Auther: Charles.Chen <br>
* Description: Mybatis返回Map<Object, Object>数据定义注解(Object为基础类型)
* Date: Create in 17:37 2018/6/5
**/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface MapToKeyValue {
/**
* 是否允许Key重复,默认true,若设置为false,且出现了重复Key,则抛出:org.springframework.dao.DuplicateKeyException
* @return
*/
boolean isAllowKeyRepeat() default true;
/**
* 是否允许value覆盖,默认true,若设置为true时,当出现Key重复且isAllowKeyRepeat = true时,将覆盖原有的value值
* @return
*/
boolean isValueOverride() default true;
/**
* Map.Key的字段名,无值默认为第一个字段值为Key
* @return
*/
String keyColumnName() default "";
/**
* Map.Value的字段名,无值默认为第二个字段值为Value
* @return
*/
String valueColumnName() default "";
}
// 自定义KeyValueClass ,若JDK环境中允许使用jfxrt.jar 中的Pair.java 则可直接使用Pair对象
/**
*
* Description: Map对象的泛型值
* Date: Create in 9:53 2018/6/7
**/
public class KeyValueClass {
Class<?> key;
Class<?> value;
public KeyValueClass(Class key, Class value) {
this.key = key;
this.value = value;
}
public Class<?> getKey() {
return key;
}
public void setKey(Class<?> key) {
this.key = key;
}
public Class<?> getValue() {
return value;
}
public void setValue(Class<?> value) {
this.value = value;
}
}
// 定义MapToKeyValueMethod
/**
* Auther: Charles.Chen <br>
* Description: 添加@MapToKeyValue注解的Method
* Date: Create in 8:37 2018/6/12
**/
class MapToKeyValueMethod {
private String id;
private KeyValueClass keyValueClass;
private MapToKeyValue mapToKeyValue;
protected MapToKeyValueMethod(){}
protected MapToKeyValueMethod(String id, KeyValueClass keyValueClass, MapToKeyValue mapToKeyValue) {
this.id = id;
this.keyValueClass = keyValueClass;
this.mapToKeyValue = mapToKeyValue;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public KeyValueClass getKeyValueClass() {
return keyValueClass;
}
public void setKeyValueClass(KeyValueClass keyValueClass) {
this.keyValueClass = keyValueClass;
}
public MapToKeyValue getMapToKeyValue() {
return mapToKeyValue;
}
public void setMapToKeyValue(MapToKeyValue mapToKeyValue) {
this.mapToKeyValue = mapToKeyValue;
}
}
// 自定义拦截器
/**
* Auther: Charles.Chen <br>
* Description: 自定义Map数据拦截器
* <p>
* <div>
* ::注意:: <br>
* <font color="red">可以使用约定优先于配置的原则,固定化Methed.Name的前缀、后缀或者Mappering.resultType类型等方式,从而减少的反射操作,从而减少资源占用</font>
* </div>
* </p>
* Date: Create in 17:53 2018/6/5
**/
@Intercepts(@Signature(method = "handleResultSets", type = ResultSetHandler.class, args = { Statement.class }))
public class MapToKeyValueInterceptor implements Interceptor {
private final static Logger logger = LoggerFactory.getLogger(MapToKeyValueInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
MetaObject metaStatementHandler = ReflectUtil.getRealTarget(invocation);
MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("mappedStatement");
logger.debug("Request:ClassName.MethodName = {}", mappedStatement.getId());
MapToKeyValueMethod currentMethod = findMethod(mappedStatement.getId());
if (currentMethod == null) {
return invocation.proceed();
}
// 获取各种TypeHander的注册器
TypeHandlerRegistry typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
// 如果有MapToKeyValue注解,则这里对结果进行拦截并转换
Statement statement = (Statement) invocation.getArgs()[0];
return resultMap(statement, typeHandlerRegistry, currentMethod);
}
@Override
public Object plugin(Object obj) {
return Plugin.wrap(obj, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 说明:缓存所有Mapper.java中的Method,key = classPath.methodName, value = MapToKeyValueMethod <br>
* 作用:降低反射的次数,提供效率 <br>
* 待优化点:Mybatis会在初始化期间通过@MapperScan扫描所有的Mapper.method,可同步获取此Map数据 <br>
*/
private static Map<String, MapToKeyValueMethod> cacheMapperMethod = new HashMap<>();
/**
* 找到与指定函数名匹配的Method。
*
* @param mappedStatementId
* @return
* @throws Throwable
*/
private MapToKeyValueMethod findMethod(String mappedStatementId) throws Throwable {
if(cacheMapperMethod.containsKey(mappedStatementId)) {
return cacheMapperMethod.get(mappedStatementId);
}
// mappedStatement.getId() == com.**.**Mapper.className.methodName
String className = StringUtils.substringBeforeLast(mappedStatementId, ".");
Method[] methods = Class.forName(className).getDeclaredMethods();
if (methods == null) {
cacheMapperMethod.put(mappedStatementId, null);
return null;
}
for (Method method : methods) {
String methodId = className + "." + method.getName();
MapToKeyValue mapToKeyValue = method.getAnnotation(MapToKeyValue.class);
if(mapToKeyValue == null) {
cacheMapperMethod.put(methodId, null);
continue;
}
KeyValueClass keyValueClass = getKVTypeOfReturnMap(method);
MapToKeyValueMethod mapToKeyValueMethod = new MapToKeyValueMethod(methodId, keyValueClass, mapToKeyValue);
cacheMapperMethod.put(className + "." + method.getName(), mapToKeyValueMethod);
}
return cacheMapperMethod.get(mappedStatementId);
}
/**
* 获取函数返回Map中key-value的类型
*
* @param method
* @return left为key的类型,right为value的类型
*/
private KeyValueClass getKVTypeOfReturnMap(Method method) {
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) returnType;
if (!Map.class.equals(parameterizedType.getRawType())) {
throw new RuntimeException(
"[ERROR-Return-Type]Use @MapToKeyValue,The return type must be \"java.util.Map\", method=" + method);
}
return new KeyValueClass((Class<?>)parameterizedType.getActualTypeArguments()[0], (Class<?>)parameterizedType.getActualTypeArguments()[1]);
}
return new KeyValueClass(String.class, String.class);
}
/**
* 将查询结果映射成Map,其中第一个字段作为key,第二个字段作为value.
*
* @param statement
* @param typeHandlerRegistry MyBatis里typeHandler的注册器,方便转换成用户指定的结果类型
* @param method 添加注解的函数
* @return
* @throws Throwable
*/
private Object resultMap(Statement statement, TypeHandlerRegistry typeHandlerRegistry, MapToKeyValueMethod method) throws Throwable {
ResultSet resultSet = statement.getResultSet();
List<Object> res = new ArrayList();
Map<Object, Object> map = new HashMap();
while (resultSet.next()) {
MapToKeyValue mapToKeyValue = method.getMapToKeyValue();
KeyValueClass keyValueClass = method.getKeyValueClass();
Object key = null;
if(mapToKeyValue.keyColumnName() == null || mapToKeyValue.keyColumnName().trim().length() == 0) {
key = this.getObject(resultSet, 1, typeHandlerRegistry, keyValueClass.getKey());
} else {
key = this.getObject(resultSet, mapToKeyValue.keyColumnName(), typeHandlerRegistry, keyValueClass.getKey());
}
Object value = null;
if(mapToKeyValue.valueColumnName() == null || mapToKeyValue.valueColumnName().trim().length() == 0) {
value = this.getObject(resultSet, 2, typeHandlerRegistry, keyValueClass.getValue());
} else {
value = this.getObject(resultSet, mapToKeyValue.valueColumnName(), typeHandlerRegistry, keyValueClass.getValue());
}
// 该key已存在
if (map.containsKey(key)) {
// 判断是否允许key重复
if (!mapToKeyValue.isAllowKeyRepeat()) {
throw new DuplicateKeyException("MapToKeyValue duplicated key! key = " + key);
}
if(!mapToKeyValue.isValueOverride()) {
continue;
}
}
map.put(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);
}
/**
* 结果类型转换。
* <p>
* 这里借用注册在MyBatis的typeHander(包括自定义的),方便进行类型转换。
*
* @param resultSet
* @param columnName 字段名
* @param typeHandlerRegistry MyBatis里typeHandler的注册器,方便转换成用户指定的结果类型
* @param javaType 要转换的Java类型
* @return
* @throws SQLException
*/
private Object getObject(ResultSet resultSet, String columnName, TypeHandlerRegistry typeHandlerRegistry,
Class<?> javaType) throws SQLException {
final TypeHandler<?> typeHandler = typeHandlerRegistry.hasTypeHandler(javaType)
? typeHandlerRegistry.getTypeHandler(javaType) : typeHandlerRegistry.getUnknownTypeHandler();
return typeHandler.getResult(resultSet, columnName);
}
}
// 获取MetaStatementHandler对象工具类
public class ReflectUtil {
/**
* 分离最后一个代理的目标对象
*
* @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;
}
}
// Spring boot 配置
1、配置Bean
@Autowired
private Interceptor[] interceptors;
@Bean
public Interceptor mapToKeyValueInterceptor() {
return new MapToKeyValueInterceptor();
}
2、配置Mybatis插件
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource bssDataSource, Interceptor[] interceptors) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(bssDataSource);
if(interceptors != null && interceptors.length != 0) {
bean.setPlugins(interceptors);
}
return bean.getObject();
}
// 使用
// Mapper.java
@MapToKeyValue
Map<String, String> selectUser();
// Mapper.xml
<select id="selectUser" resultType="java.util.Map">
select id as "key", `name` as 'value' from USER
</select>
转载于:https://my.oschina.net/clyy/blog/1825663