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...相关推荐

  1. sql一个表中两个字段合并求和

    sql一个表中两个字段,合并求和 SELECT SUM(字段a+'.'+字段b) as total  from TABLE 转载于:https://www.cnblogs.com/lovewyc131 ...

  2. SQL查询列名,类型,字段类型

    SQL查询列名,类型,字段类型 SELECTCOLUMN_NAME 列名,COLUMN_TYPE 数据类型,DATA_TYPE 字段类型,CHARACTER_MAXIMUM_LENGTH 长度,IS_ ...

  3. MySQL数据通过SQL查询指定数据表的字段名及字段备注

    MySQL数据通过SQL查询指定数据表的字段名及字段备注 SELECT COLUMN_NAME,COLUMN_COMMENT FROM INFORMATION_SCHEMA.Columns WHERE ...

  4. mybatis删除成功返回0_你还在用分页?试试 MyBatis 流式查询,真心强大!

    转自:捏造的信仰 segmentfault.com/a/1190000022478915 基本概念 流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果.流式查询 ...

  5. SQL查询优化方法 提高SQL查询效率 数据库的哪些字段适合添加索引

    如何提高sql的查询效率 在正确的字段上创建索引. 优化查询sql的写法(特别是where语句的写法). 一.数据库的哪些字段适合添加索引 表的某个字段值得离散度越高,该字段越适合选作索引的关键字.主 ...

  6. SQL查询一个表中类别字段中Max()最大值对应的记录

    问题是: 数据库有一个表 code,里面有个点击量字段click_num和一个类别字段kind以及其它信息字段, 现在要搜出每个类别中点击量最大的那条记录,如果是10个类别,那么结果应该是10条记录, ...

  7. MyBatis的sql动态传入表名和字段名,并判断是否为空

    id:方法名 parameterType:入参类型 resultType:返回类型,默认map statementType:预编译,还是非预编译 预编译:PRESTATEMENT,在系统初始化时就会读 ...

  8. SQL语句order by两个字段同时排序

    ORDER BY  后可加2个字段,用英文逗号隔开. f1用升序, f2降序,sql该这样写 ORDER BY  f1, f2  DESC 也可以这样写,更清楚: ORDER BY  f1 ASC, ...

  9. SQL 查询重复数据,多字段联合重复

    SELECT * FROM pagelist a WHERE ((SELECT COUNT(*) FROM pagelist WHERE ntitle = a.ntitle) > 1) ORDE ...

最新文章

  1. TensorFlow反向传播算法实现
  2. $httpprovider指令中拦截器interceptors的使用介绍
  3. 读书笔记--C陷阱与缺陷(三)
  4. 领域驱动设计:软件核心复杂性应对之道
  5. python自学教程变量_Python学习入门基础教程(learning Python)--2.2.1 Python下的变量解析...
  6. 建模步骤_【设计课堂】游戏手柄建模,看这14个步骤图就够了!
  7. flink的savepoint实验-java
  8. 信息学奥赛一本通 1011:甲流疫情死亡率 | OpenJudge NOI 1.3 06
  9. python获取微信用户基本信息_微信开放平台扫码登录获取用户基本信息!附可用demo...
  10. 复用管脚_如何实现UART的分时复用
  11. A joke about regular expression
  12. myeclipse下载_资源共享:常用的编程软件下载链接分享
  13. 【笔记总结】C陷阱与缺陷
  14. ubuntu如何安装本地deb文件
  15. python怎么用sin_python怎么打sin
  16. 3个月红酒上千万的营业额:新模式下的营销奥秘
  17. iscsi 网络存储
  18. 基于python的QQ第三方登陆实现工具类
  19. Cppcheck 1.54 C/C++静态代码分析工具
  20. 脑机接口技术的现状与未来!

热门文章

  1. Docker简单入门
  2. 做游戏,学编程(C语言) 2 最简单的飞机游戏
  3. 3认证老外主任_首批18款App认证名单公布 未来将开展数据安全管理认证
  4. 机战 mysql_机战完整端-加配套网站-架设说明-配套工具
  5. 特斯拉、华为等竞争升级,国产芯片实现突破,自动驾驶过去这一年
  6. 都 2021 了,你还忘记关闭 http body?
  7. 如何设计一个能够扩展到百万用户的系统?
  8. SUSE梁胜:程序员涨薪飞快,做开源贡献者没有意义
  9. BAT大牛这样搞Python,真是绝了
  10. 一年翻 3 倍,装机量 6 亿台的物联网操作系统又放大招!