Mybatis返回Map的一种实现

前言

在使用Mybatis进行系统开发的时候,有时候我们会有这么一种需求:我们希望通过Mybatis查询某一个表返回的结果是一个Map,而这个Map的Key是表的一个字段,Value是另一个字段。然而当我们按照Mybatis的做法,指定查询Mapper语句的resultType为map时返回的结果是一个Map列表(表中有多条记录时),而且每个元素Map对应的是表的一行记录(Key为每个字段的名称,Value为对应的值),这跟我们的需求是不相符合的。那有什么方法可以让Mybatis查询出来的结果是一个Key为表中某一列的值,Value为表中另一列的值的Map呢?今天将跟大伙介绍一种使用Mybatis拦截器来实现查询返回Map的方法。关于Mybatis拦截器的介绍可以查看这篇博客。

实现

返回结果是由ResultSetHandler的handleResultSets方法对当前的Statement处理后的返回结果,所以我们如果要改变返回结果的话就需要使用Mybatis的拦截器对ResultSetHandler接口的handleResultSets方法进行拦截。

我们不可能把所有的ResultSet都拦截,然后返回对应的Map,所以我们需要在拦截到handleResultSets方法之后进行筛选,对于不需要拦截的调用Invocation的proceed()方法,而需要拦截的则实现我们自己的逻辑,返回对应的结果。对于这种筛选条件一般都是通过ParameterObject来进行的。这里我自定义了一个类,叫MapParam,当某一个语句需要返回一个Map时,就指定其参数类型为MapParam。

这个参数类型MapParam一定需要有三个作用:

  • (1)    可以指定哪个字段为返回Map的Key;
  • (2)    可以指定哪个字段为返回Map的Value;
  • (3)    可以附带其他参数;

综合以上三点考虑,我觉得比较合适的MapParam应该是一个Map,当然这个Map是特用的,或者其包含一个用于存放参数的Map的引用。这里我选择使用第一种方式,定义一个继承自HashMap的类。

Java代码  
  1. public class MapParam extends HashMap<String, Object> {
  2. /**
  3. *
  4. */
  5. private static final long serialVersionUID = 1L;
  6. /**
  7. * 作为Key的字段对应MapParam的Key
  8. */
  9. public static final String  KEY_FIELD = "mapKeyField";
  10. /**
  11. * 作为Value的字段对应MapParam的Key
  12. */
  13. public static final String VALUE_FIELD = "mapValueField";
  14. public MapParam() {
  15. }
  16. /**
  17. * 指定keyField和valueField
  18. * @param keyField Map中key对应的字段
  19. * @param valueField Map中value对应的字段
  20. */
  21. public MapParam(String keyField, String valueField) {
  22. this.put(KEY_FIELD, keyField);
  23. this.put(VALUE_FIELD, valueField);
  24. }
  25. }

从上面定义的这个参数类我们可以看到,我们定义了使用哪个字段作为返回结果的Key,哪个字段作为返回结果的Value,同时我们还可以往里面put其他参数供SQL使用。

接着就是来实现我们的拦截器了。这里我们定义一个MapInterceptor用于拦截对应的结果集返回一个Map。其代码如下所示:

Java代码  
  1. @Intercepts(@Signature(method="handleResultSets", type=ResultSetHandler.class, args={Statement.class}))
  2. public class MapInterceptor implements Interceptor {
  3. /* (non-Javadoc)
  4. * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
  5. */
  6. public Object intercept(Invocation invocation) throws Throwable {
  7. //通过invocation获取代理的目标对象
  8. Object target = invocation.getTarget();
  9. //暂时ResultSetHandler只有FastResultSetHandler这一种实现
  10. if (target instanceof FastResultSetHandler) {
  11. FastResultSetHandler resultSetHandler = (FastResultSetHandler) target;
  12. //利用反射获取到FastResultSetHandler的ParameterHandler属性,从而获取到ParameterObject;
  13. ParameterHandler parameterHandler = ReflectUtil.getFieldValue(resultSetHandler, "parameterHandler");
  14. Object parameterObj = parameterHandler.getParameterObject();
  15. //判断ParameterObj是否是我们定义的MapParam,如果是则进行自己的处理逻辑
  16. if (parameterObj instanceof MapParam) {//拦截到了
  17. MapParam mapParam = (MapParam) parameterObj;
  18. //获取到当前的Statement
  19. Statement stmt = (Statement) invocation.getArgs()[0];
  20. //通过Statement获取到当前的结果集,对其进行处理,并返回对应的处理结果
  21. return handleResultSet(stmt.getResultSet(), mapParam);
  22. }
  23. }
  24. //如果没有进行拦截处理,则执行默认逻辑
  25. return invocation.proceed();
  26. }
  27. /**
  28. * 处理结果集
  29. * @param resultSet
  30. * @param mapParam
  31. * @return
  32. */
  33. private Object handleResultSet(ResultSet resultSet, MapParam mapParam) {
  34. // TODO Auto-generated method stub
  35. if (resultSet != null) {
  36. //拿到Key对应的字段
  37. String keyField = (String) mapParam.get(MapParam.KEY_FIELD);
  38. //拿到Value对应的字段
  39. String valueField = (String) mapParam.get(MapParam.VALUE_FIELD);
  40. //定义用于存放Key-Value的Map
  41. Map<Object, Object> map = new HashMap<Object, Object>();
  42. //handleResultSets的结果一定是一个List,当我们的对应的Mapper接口定义的是返回一个单一的元素,并且handleResultSets返回的列表
  43. //的size为1时,Mybatis会取返回的第一个元素作为对应Mapper接口方法的返回值。
  44. List<Object> resultList = new ArrayList<Object>();
  45. try {
  46. //把每一行对应的Key和Value存放到Map中
  47. while (resultSet.next()) {
  48. Object key = resultSet.getObject(keyField);
  49. Object value = resultSet.getObject(valueField);
  50. map.put(key, value);
  51. }
  52. } catch (SQLException e) {
  53. e.printStackTrace();
  54. } finally {
  55. closeResultSet(resultSet);
  56. }
  57. //把封装好的Map存放到List中并进行返回
  58. resultList.add(map);
  59. return resultList;
  60. }
  61. return null;
  62. }
  63. /**
  64. * 关闭ResultSet
  65. * @param resultSet 需要关闭的ResultSet
  66. */
  67. private void closeResultSet(ResultSet resultSet) {
  68. try {
  69. if (resultSet != null) {
  70. resultSet.close();
  71. }
  72. } catch (SQLException e) {
  73. }
  74. }
  75. /* (non-Javadoc)
  76. * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
  77. */
  78. public Object plugin(Object obj) {
  79. return Plugin.wrap(obj, this);
  80. }
  81. /* (non-Javadoc)
  82. * @see org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
  83. */
  84. public void setProperties(Properties props) {
  85. }
  86. }

拦截器的实现这一块就没什么好讲的了,就是简单的把Statement对应的结果集封装成一个Map,再把用于返回的Map封装成一个List进行返回。里面的代码很简单,相信大伙都看得懂。而对于Mybatis拦截器不懂的可以看这篇文章。

在上面拦截器的实现中用到了一个工具类ReflectUtil,其代码如下所示:

Java代码  
  1. public class ReflectUtil {
  2. /**
  3. * 利用反射获取指定对象的指定属性
  4. * @param obj 目标对象
  5. * @param fieldName 目标属性
  6. * @return 目标属性的值
  7. */
  8. @SuppressWarnings("unchecked")
  9. public static <T> T getFieldValue(Object obj, String fieldName) {
  10. Object result = null;
  11. Field field = ReflectUtil.getField(obj, fieldName);
  12. if (field != null) {
  13. field.setAccessible(true);
  14. try {
  15. result = field.get(obj);
  16. } catch (IllegalArgumentException e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. } catch (IllegalAccessException e) {
  20. // TODO Auto-generated catch block
  21. e.printStackTrace();
  22. }
  23. }
  24. return (T)result;
  25. }
  26. /**
  27. * 利用反射获取指定对象里面的指定属性
  28. * @param obj 目标对象
  29. * @param fieldName 目标属性
  30. * @return 目标字段
  31. */
  32. private static Field getField(Object obj, String fieldName) {
  33. Field field = null;
  34. for (Class<?> clazz=obj.getClass(); clazz != Object.class; clazz=clazz.getSuperclass()) {
  35. try {
  36. field = clazz.getDeclaredField(fieldName);
  37. break;
  38. } catch (NoSuchFieldException e) {
  39. //这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
  40. }
  41. }
  42. return field;
  43. }
  44. /**
  45. * 利用反射设置指定对象的指定属性为指定的值
  46. * @param obj 目标对象
  47. * @param fieldName 目标属性
  48. * @param fieldValue 目标值
  49. */
  50. public static void setFieldValue(Object obj, String fieldName,
  51. String fieldValue) {
  52. Field field = ReflectUtil.getField(obj, fieldName);
  53. if (field != null) {
  54. try {
  55. field.setAccessible(true);
  56. field.set(obj, fieldValue);
  57. } catch (IllegalArgumentException e) {
  58. // TODO Auto-generated catch block
  59. e.printStackTrace();
  60. } catch (IllegalAccessException e) {
  61. // TODO Auto-generated catch block
  62. e.printStackTrace();
  63. }
  64. }
  65. }
  66. }

至此,使用Mybatis拦截器实现Mybatis返回Map的方法就介绍的差不多了,接下来我们举一个例子来说一下它的应用。

应用

首先,我们需要把它配置起来,让Mybatis能够对它进行注册。配置的方法很简单,就是在Mybatis的配置文件中利用plugins元素下面的plugin进行注册。

Xml代码  
  1. <plugins>
  2. <plugin interceptor="com.tiantian.mybatis.interceptor.MapInterceptor"/>
  3. </plugins>

而对于使用Spring整合Mybatis的用户来说,如果你在整合的时候是把Mybatis的配置都放在Mybatis的配置文件中,然后通过Spring配置文件中的bean SqlSessionFactoryBean的configLocation来指定这个配置文件的位置的话你的配置依然可以像上面那样写在Mybatis的配置文件中;但如果你是通过SqlSessionFactoryBean的属性来指定Mybatis配置文件中的一些内容的话,那么你的拦截器应该是通过SqlSessionFactoryBean的plugins属性来指定的,你的配置文件看起来大概是这样子:

Xml代码  
  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  2. ……
  3. <property name="plugins">
  4. <array>
  5. <bean class="com.tiantian.mybatis.interceptor.MapInterceptor"/>
  6. </array>
  7. </property>
  8. ……
  9. </bean>

配置好以后我们就可以使用该拦截器进行相应的拦截了。

这里假设我们有一个表t_city,该表一共有三个字段,id、code和name。现在我们希望通过Mybatis来查询该表时返回结果是一个Map,其中key为每行字段code的值,value为对应字段name的值。这个时候我们的Mapper语句大概是这样:

Xml代码  
  1. <select id="find" resultType="map" parameterType="MapParam">
  2. select code, name from t_city
  3. </select>

其对应的Mapper接口方法大概是这样:

Java代码  
  1. public interface CityMapper extends SuperMapper {
  2. public Map<Object, Object> find(MapParam param);
  3. }

那么,当我们去调用的时候大概是这样调用的,准备好一个参数对象MapParam,赋好对应的属性之后把它传递给对应的Mapper接口方法:

Java代码  
  1. @Test
  2. public void testMap() {
  3. MapParam param = new MapParam("code", "name");
  4. Map<Object, Object> result = cityMapper.find(param);
  5. System.out.println(result);
  6. }

说明

在这篇博客中,我只是介绍使用Mybatis拦截器实现Mybatis查询返回Map的这么一种方法,里面的代码考虑是比较粗糙的,想要在使用Mybatis拦截器拦截到ResultSetHandler的handleResultSets方法,对ResultSet做更加严谨的处理的话,推荐参考Mybatis ResultSetHandler接口的实现类FastResultSetHandler关于handleResultSets(Statement stmt)的实现。

Mybatis返回Map的一种实现相关推荐

  1. 12、mybatis返回map单条及多条记录

    文章目录 1.mybatis返回map单条记录 1).EmployeeMapper接口 2).EmployeeMapper.xml 3).Test 4).测试结果 2.mybatis返回map多条记录 ...

  2. java mybatis 返回map_mybatis返回map集合的格式是什么?mybatis返回map集合实例

    Mybatis因为会与数据库交互,所以经常会有返回map集合的场景,那一般mybatis返回map集合的格式是什么呢?下面小编就用一些实例与你分享分享吧. 例1:返回key不定:返回key为学员id, ...

  3. Mybatis返回Map

    返回一条记录的map resultType="map"   key就是列名,值就是对应的值 多条记录封装成一个map Select返回类型中是返回Map时,是对方法中是否存在注解@ ...

  4. mybatis返回map键值对_mybatis返回map结果集怎么配置

    匿名用户 1级 2018-06-10 回答 一.概述 MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返 ...

  5. mybatis返回map键值对_mybatis返回map key怎么指定

    展开全部 一.概述 MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返回类型的,而resultMap则是 ...

  6. mybatis返回map操作

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...

  7. 若依mybatis返回map将下划线命名转为驼峰式命名

    默认情况,若依不支持这个配置,需要我们重写配置类才能到达需求. 目录 一.需求情景复现 二.重写配置类 (1)导入guava依赖 (2)  编写配置类

  8. MyBatis查询两个字段,返回Map,一个字段作为key,一个字段作为value的实现

    1. 问题描述 在使用MyBatis,我们经常会遇到这种情况:SELECT两个字段,需要返回一个Map,其中第一个字段作为key,第二个字段作为value.MyBatis的MapKey虽然很实用,但并 ...

  9. 【mybatis】mybatis中 返回map集合

    关于mybatis返回map集合的操作: 1.mapper.xml中写一个查询返回map的sql <select id="findMap" parameterType=&qu ...

最新文章

  1. 【题单 - 数学专题】最大公约数
  2. 重温目标检测--SSD
  3. 在虚幻UE4中不同VR头盔的FOV和分屏处理
  4. 主流Java数据库连接池比较及前瞻
  5. Android中对话框的工具类
  6. hdu 1698 Just a Hook 线段树区间更新
  7. POJ 2777 Count Color (线段树区间修改 + 状态压缩)
  8. php递归5,5.5.1 PHP递归函数
  9. Mega网盘来下载外国友人分享的资源
  10. 首都师范 博弈论 5 4 4 多人合作博弈问题 Shapley计算之财产分配问题
  11. 特征探索性分析Exploring_features
  12. 杰理之AUDIO_DAC【篇】
  13. Java-事务的传播特性和隔离级别
  14. 【C语言】- 关机小程序
  15. 转-思维要裂变要敢闯想
  16. IDEA导入scala详解
  17. 实验一 基本逻辑门电路
  18. 10个良心的软件免费下载网站,你知道吗?
  19. 解决删除文件时出现“该项目不在XX中,请确认该项目的位置然后重试”的提示
  20. 猫游记服务器维护后露娜,猫游记官方网站

热门文章

  1. 下载国家自然科学基金结题报告的免费工具
  2. WPF 控件【L】ListView(一) ListView如何实现单行SelectedItem或多行SelectedItems的绑定
  3. 博客搬迁至Gitcafe
  4. 用PS打造无限旋转阶梯级
  5. 客快物流大数据项目(一百零一):实时OLAP开发
  6. 智慧物流:物流行业下一个风口,仓储转运全方位掌控
  7. 关于ITIL Foundation和ITIL Expert的考证路径介绍
  8. 离散数学知识框架小结
  9. Java云电子病历源码:电子病历在线编辑
  10. 打开visio2013 32位报VisProWW.MSI的文件夹路径错误