Mybatis返回Map的一种实现

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.      */  
  6.     private static final long serialVersionUID = 1L;  
  7.   
  8.     /** 
  9.      * 作为Key的字段对应MapParam的Key 
  10.      */  
  11.     public static final String  KEY_FIELD = "mapKeyField";  
  12.     /** 
  13.      * 作为Value的字段对应MapParam的Key 
  14.      */  
  15.     public static final String VALUE_FIELD = "mapValueField";  
  16.       
  17.     public MapParam() {  
  18.           
  19.     }  
  20.       
  21.     /** 
  22.      * 指定keyField和valueField 
  23.      * @param keyField Map中key对应的字段 
  24.      * @param valueField Map中value对应的字段 
  25.      */  
  26.     public MapParam(String keyField, String valueField) {  
  27.         this.put(KEY_FIELD, keyField);  
  28.         this.put(VALUE_FIELD, valueField);  
  29.     }  
  30.       
  31. }  

  

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

 

       拦截器的实现这一块就没什么好讲的了,就是简单的把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.      * 利用反射获取指定对象里面的指定属性 
  29.      * @param obj 目标对象 
  30.      * @param fieldName 目标属性 
  31.      * @return 目标字段 
  32.      */  
  33.     private static Field getField(Object obj, String fieldName) {  
  34.         Field field = null;  
  35.         for (Class<?> clazz=obj.getClass(); clazz != Object.class; clazz=clazz.getSuperclass()) {  
  36.             try {  
  37.                 field = clazz.getDeclaredField(fieldName);  
  38.                 break;  
  39.             } catch (NoSuchFieldException e) {  
  40.                 //这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。  
  41.             }  
  42.         }  
  43.         return field;  
  44.     }  
  45.   
  46.     /** 
  47.      * 利用反射设置指定对象的指定属性为指定的值 
  48.      * @param obj 目标对象 
  49.      * @param fieldName 目标属性 
  50.      * @param fieldValue 目标值 
  51.      */  
  52.     public static void setFieldValue(Object obj, String fieldName,  
  53.             String fieldValue) {  
  54.         Field field = ReflectUtil.getField(obj, fieldName);  
  55.         if (field != null) {  
  56.             try {  
  57.                 field.setAccessible(true);  
  58.                 field.set(obj, fieldValue);  
  59.             } catch (IllegalArgumentException e) {  
  60.                 // TODO Auto-generated catch block  
  61.                 e.printStackTrace();  
  62.             } catch (IllegalAccessException e) {  
  63.                 // TODO Auto-generated catch block  
  64.                 e.printStackTrace();  
  65.             }  
  66.         }  
  67.     }  
  68. }  

       至此,使用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.    
  3.     public Map<Object, Object> find(MapParam param);  
  4.       
  5. }  

 

       那么,当我们去调用的时候大概是这样调用的,准备好一个参数对象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)的实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MyBatis 可以返回 Map 类型的结果,其中键(Key)为列名,值(Value)为对应的数据行中的值。返回 Map 的方法有两种方式: 1. 返回单个 Map 类型的结果 ```java Map<String, Object> resultMap = sqlSession.selectOne("selectUserById", 1); ``` 其中,`selectOne` 方法返回单个结果,`selectUserById` 是 SQL 映射文件中定义的查询语句的 ID,1 是查询参数。 2. 返回多个 Map 类型的结果 ```java List<Map<String, Object>> resultList = sqlSession.selectList("selectAllUsers"); ``` 其中,`selectList` 方法返回多个结果,`selectAllUsers` 是 SQL 映射文件中定义的查询语句的 ID。 无论是返回单个还是多个 Map 类型的结果,MyBatis 都会将查询结果映射到 Map 对象中,其中键为列名,值为对应的数据行中的值。 ### 回答2: MyBatis一种常用的Java持久化框架,可以用于通过SQL语句与数据库进行交互。当你使用MyBatis查询数据库时,有时需要返回一个Map对象来保存查询结果。 MyBatis提供了多种返回结果类型,其中返回Map对象是一种常见的选择。你可以通过配置来告诉MyBatis使用Map作为返回结果类型,并指定Map的键和值。 首先,在MyBatis的映射文件(Mapper XML)中,你需要为你的查询语句配置使用Map返回结果。在select标签的resultType属性中,可以指定一个Map类型的别名或全限定名,表示使用Map作为返回结果。例如: ``` <select id="getUserInfo" resultType="java.util.Map"> SELECT * FROM users WHERE id = #{id} </select> ``` 上述代码表示使用java.util.Map作为查询结果的返回类型。 然后,在应用程序的代码中调用相应的查询方法。当MyBatis执行查询并获取结果时,它将把查询结果映射到一个Map对象中,其中键为列名,值为对应的数据值。 例如,如果查询结果有两列,分别是"id"和"name",那么返回Map对象将包含两个键值对,如下所示: ```java Map<String, Object> resultMap = sqlSession.selectOne("getUserInfo", 1); System.out.println(resultMap.get("id")); // 输出查询结果中的id值 System.out.println(resultMap.get("name")); // 输出查询结果中的name值 ``` 通过上述代码,你可以根据列名获取查询结果的值。 总结来说,MyBatis可以返回Map对象作为查询结果,你需要在映射文件中配置使用Map类型,并在代码中获取Map对象后根据键名获取对应的值。这样可以灵活地处理查询结果,并方便地进行数据操作。 ### 回答3: MyBatis是一个流行的Java持久化框架,可以与关系数据库进行交互。在MyBatis中,可以使用多种方式来返回查询结果,其中一种常用的方式是返回Map对象。 返回Map的操作步骤如下: 1. 首先,在MyBatis的映射文件中,编写SQL语句,并使用ResultMap来映射查询结果。可以使用resultType属性来指定返回结果的类型为Map。 2. 在Java代码中,通过调用MyBatis提供的查询方法来执行SQL语句,获取查询结果。 3. 当查询结果返回时,MyBatis会根据ResultMap的定义,将查询结果映射到一个Map对象中。 4. 可以通过调用Map的get()方法,根据指定的键获取相应的值。 使用返回Map的好处是可以方便地获取查询结果中的字段值,因为Map是一个键值对合,可以通过键来获取对应的值。同时,返回Map还可以保留查询结果中的字段名与字段值之间的映射关系。 需要注意的是,当查询结果中存在多行记录时,返回Map对象会包含多个键值对,可以通过遍历Map对象,获取每行记录的字段值。 总之,MyBatis提供了返回Map的功能,可以方便地获取查询结果的字段值,并保留字段名与字段值之间的映射关系。这是MyBatis灵活性的体现之一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值