mysql2个字段还会map_通过注解实现MyBatis将sql查询结果的两个字段分别作为map的key,value...
package com.lsz.config.enums;
import java.lang.annotation.*;
/**
* 将查询结果映射成map的注解,其中第一个字段为key,第二个字段为value.
* 注:返回类型必须为{@link java.util.Map Map}。K/V的类型通过MyBatis的TypeHander进行类型转换,如有必要可自定义TypeHander。
*
* @author lishuzhen
* @date 2020/11/2 13:40
*/
@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;
}
定义一个拦截器
package com.lsz.config.interceptor;
import com.lsz.config.enums.MapF2F;
import com.lsz.config.utils.ReflectUtil;
import javafx.util.Pair;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.dao.DuplicateKeyException;
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.*;
/**
* @author lishuzhen
* @date 2020/11/2 15:36
*/
@Intercepts(@Signature(method = "handleResultSets", type = ResultSetHandler.class, args = {Statement.class}))
public class MapF2FInterceptor implements Interceptor {
@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
Method currentMethod = findMethod(className, currentMethodName);
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];
// 获取返回Map里key-value的类型
Pair, Class>> kvTypePair = getKVTypeOfReturnMap(currentMethod);
// 获取各种TypeHander的注册器
TypeHandlerRegistry typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
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>> 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>> kvTypePair, MapF2F mapF2FAnnotation) throws Throwable {
ResultSet resultSet = statement.getResultSet();
List res = new ArrayList();
Map 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);
}
}
// 第一列作为key,第二列作为value。
map.put(key, value);
}
res.add(map);
return res;
}
/**
* 结果类型转换。
*
* 这里借用注册在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);
}
}
定义一个处理反射的工具类
mysql2个字段还会map_通过注解实现MyBatis将sql查询结果的两个字段分别作为map的key,value...相关推荐
- sql一个表中两个字段合并求和
sql一个表中两个字段,合并求和 SELECT SUM(字段a+'.'+字段b) as total from TABLE 转载于:https://www.cnblogs.com/lovewyc131 ...
- SQL查询列名,类型,字段类型
SQL查询列名,类型,字段类型 SELECTCOLUMN_NAME 列名,COLUMN_TYPE 数据类型,DATA_TYPE 字段类型,CHARACTER_MAXIMUM_LENGTH 长度,IS_ ...
- MySQL数据通过SQL查询指定数据表的字段名及字段备注
MySQL数据通过SQL查询指定数据表的字段名及字段备注 SELECT COLUMN_NAME,COLUMN_COMMENT FROM INFORMATION_SCHEMA.Columns WHERE ...
- mybatis删除成功返回0_你还在用分页?试试 MyBatis 流式查询,真心强大!
转自:捏造的信仰 segmentfault.com/a/1190000022478915 基本概念 流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果.流式查询 ...
- SQL查询优化方法 提高SQL查询效率 数据库的哪些字段适合添加索引
如何提高sql的查询效率 在正确的字段上创建索引. 优化查询sql的写法(特别是where语句的写法). 一.数据库的哪些字段适合添加索引 表的某个字段值得离散度越高,该字段越适合选作索引的关键字.主 ...
- SQL查询一个表中类别字段中Max()最大值对应的记录
问题是: 数据库有一个表 code,里面有个点击量字段click_num和一个类别字段kind以及其它信息字段, 现在要搜出每个类别中点击量最大的那条记录,如果是10个类别,那么结果应该是10条记录, ...
- MyBatis的sql动态传入表名和字段名,并判断是否为空
id:方法名 parameterType:入参类型 resultType:返回类型,默认map statementType:预编译,还是非预编译 预编译:PRESTATEMENT,在系统初始化时就会读 ...
- SQL语句order by两个字段同时排序
ORDER BY 后可加2个字段,用英文逗号隔开. f1用升序, f2降序,sql该这样写 ORDER BY f1, f2 DESC 也可以这样写,更清楚: ORDER BY f1 ASC, ...
- SQL 查询重复数据,多字段联合重复
SELECT * FROM pagelist a WHERE ((SELECT COUNT(*) FROM pagelist WHERE ntitle = a.ntitle) > 1) ORDE ...
最新文章
- TensorFlow反向传播算法实现
- $httpprovider指令中拦截器interceptors的使用介绍
- 读书笔记--C陷阱与缺陷(三)
- 领域驱动设计:软件核心复杂性应对之道
- python自学教程变量_Python学习入门基础教程(learning Python)--2.2.1 Python下的变量解析...
- 建模步骤_【设计课堂】游戏手柄建模,看这14个步骤图就够了!
- flink的savepoint实验-java
- 信息学奥赛一本通 1011:甲流疫情死亡率 | OpenJudge NOI 1.3 06
- python获取微信用户基本信息_微信开放平台扫码登录获取用户基本信息!附可用demo...
- 复用管脚_如何实现UART的分时复用
- A joke about regular expression
- myeclipse下载_资源共享:常用的编程软件下载链接分享
- 【笔记总结】C陷阱与缺陷
- ubuntu如何安装本地deb文件
- python怎么用sin_python怎么打sin
- 3个月红酒上千万的营业额:新模式下的营销奥秘
- iscsi 网络存储
- 基于python的QQ第三方登陆实现工具类
- Cppcheck 1.54 C/C++静态代码分析工具
- 脑机接口技术的现状与未来!