欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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