`
234390216
  • 浏览: 10238595 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
博客专栏
A5ee55b9-a463-3d09-9c78-0c0cf33198cd
Oracle基础
浏览量:462885
Ad26f909-6440-35a9-b4e9-9aea825bd38e
springMVC介绍
浏览量:1775976
Ce363057-ae4d-3ee1-bb46-e7b51a722a4b
Mybatis简介
浏览量:1398738
Bdeb91ad-cf8a-3fe9-942a-3710073b4000
Spring整合JMS
浏览量:395157
5cbbde67-7cd5-313c-95c2-4185389601e7
Ehcache简介
浏览量:680171
Cc1c0708-ccc2-3d20-ba47-d40e04440682
Cas简介
浏览量:531143
51592fc3-854c-34f4-9eff-cb82d993ab3a
Spring Securi...
浏览量:1185006
23e1c30e-ef8c-3702-aa3c-e83277ffca91
Spring基础知识
浏览量:468811
4af1c81c-eb9d-365f-b759-07685a32156e
Spring Aop介绍
浏览量:151491
2f926891-9e7a-3ce2-a074-3acb2aaf2584
JAXB简介
浏览量:68342
社区版块
存档分类
最新评论

Mybatis返回Map的一种实现

阅读更多

Mybatis返回Map的一种实现

前言

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

实现

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

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

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

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

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

public class MapParam extends HashMap<String, Object> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * 作为Key的字段对应MapParam的Key
	 */
	public static final String  KEY_FIELD = "mapKeyField";
	/**
	 * 作为Value的字段对应MapParam的Key
	 */
	public static final String VALUE_FIELD = "mapValueField";
	
	public MapParam() {
		
	}
	
	/**
	 * 指定keyField和valueField
	 * @param keyField Map中key对应的字段
	 * @param valueField Map中value对应的字段
	 */
	public MapParam(String keyField, String valueField) {
		this.put(KEY_FIELD, keyField);
		this.put(VALUE_FIELD, valueField);
	}
	
}

  

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

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

@Intercepts(@Signature(method="handleResultSets", type=ResultSetHandler.class, args={Statement.class}))
public class MapInterceptor implements Interceptor {

	/* (non-Javadoc)
	 * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
	 */
	public Object intercept(Invocation invocation) throws Throwable {
		//通过invocation获取代理的目标对象
		Object target = invocation.getTarget();
		//暂时ResultSetHandler只有FastResultSetHandler这一种实现
		if (target instanceof FastResultSetHandler) {
			FastResultSetHandler resultSetHandler = (FastResultSetHandler) target;
			//利用反射获取到FastResultSetHandler的ParameterHandler属性,从而获取到ParameterObject;
			ParameterHandler parameterHandler = ReflectUtil.getFieldValue(resultSetHandler, "parameterHandler");
			Object parameterObj = parameterHandler.getParameterObject();
			//判断ParameterObj是否是我们定义的MapParam,如果是则进行自己的处理逻辑
			if (parameterObj instanceof MapParam) {//拦截到了
				MapParam mapParam = (MapParam) parameterObj;
				//获取到当前的Statement
				Statement stmt = (Statement) invocation.getArgs()[0];
				//通过Statement获取到当前的结果集,对其进行处理,并返回对应的处理结果
				return handleResultSet(stmt.getResultSet(), mapParam);
			}
		}
		//如果没有进行拦截处理,则执行默认逻辑
		return invocation.proceed();
	}

	/**
	 * 处理结果集
	 * @param resultSet
	 * @param mapParam
	 * @return
	 */
	private Object handleResultSet(ResultSet resultSet, MapParam mapParam) {
		// TODO Auto-generated method stub
		if (resultSet != null) {
			//拿到Key对应的字段
			String keyField = (String) mapParam.get(MapParam.KEY_FIELD);
			//拿到Value对应的字段
			String valueField = (String) mapParam.get(MapParam.VALUE_FIELD);
			//定义用于存放Key-Value的Map
			Map<Object, Object> map = new HashMap<Object, Object>();
			//handleResultSets的结果一定是一个List,当我们的对应的Mapper接口定义的是返回一个单一的元素,并且handleResultSets返回的列表
			//的size为1时,Mybatis会取返回的第一个元素作为对应Mapper接口方法的返回值。
			List<Object> resultList = new ArrayList<Object>();
			try {
				//把每一行对应的Key和Value存放到Map中
				while (resultSet.next()) {
					Object key = resultSet.getObject(keyField);
					Object value = resultSet.getObject(valueField);
					map.put(key, value);
				}
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				closeResultSet(resultSet);
			}
			//把封装好的Map存放到List中并进行返回
			resultList.add(map);
			return resultList;
		}
		return null;
	}

	/**
	 * 关闭ResultSet
	 * @param resultSet 需要关闭的ResultSet
	 */
	private void closeResultSet(ResultSet resultSet) {
		try {
			if (resultSet != null) {
				resultSet.close();
			}
		} catch (SQLException e) {
			
		}
	}

	/* (non-Javadoc)
	 * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
	 */
	public Object plugin(Object obj) {
		return Plugin.wrap(obj, this);
	}

	/* (non-Javadoc)
	 * @see org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
	 */
	public void setProperties(Properties props) {
		
	}

}

 

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

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

 

public class ReflectUtil {
	/**
	 * 利用反射获取指定对象的指定属性
	 * @param obj 目标对象
	 * @param fieldName 目标属性
	 * @return 目标属性的值
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getFieldValue(Object obj, String fieldName) {
		Object result = null;
		Field field = ReflectUtil.getField(obj, fieldName);
		if (field != null) {
			field.setAccessible(true);
			try {
				result = field.get(obj);
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return (T)result;
	}
	
	/**
	 * 利用反射获取指定对象里面的指定属性
	 * @param obj 目标对象
	 * @param fieldName 目标属性
	 * @return 目标字段
	 */
	private static Field getField(Object obj, String fieldName) {
		Field field = null;
		for (Class<?> clazz=obj.getClass(); clazz != Object.class; clazz=clazz.getSuperclass()) {
			try {
				field = clazz.getDeclaredField(fieldName);
				break;
			} catch (NoSuchFieldException e) {
				//这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
			}
		}
		return field;
	}

	/**
	 * 利用反射设置指定对象的指定属性为指定的值
	 * @param obj 目标对象
	 * @param fieldName 目标属性
	 * @param fieldValue 目标值
	 */
	public static void setFieldValue(Object obj, String fieldName,
			String fieldValue) {
		Field field = ReflectUtil.getField(obj, fieldName);
		if (field != null) {
			try {
				field.setAccessible(true);
				field.set(obj, fieldValue);
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

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

应用

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

 

    <plugins>
       <plugin interceptor="com.tiantian.mybatis.interceptor.MapInterceptor"/>
    </plugins>

 

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

 

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
       ……
       <property name="plugins">
           <array>
              <bean class="com.tiantian.mybatis.interceptor.MapInterceptor"/>
           </array>
       </property>
        ……
    </bean>

 

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

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

    <select id="find" resultType="map" parameterType="MapParam">
       select code, name from t_city
    </select>

 

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

 

public interface CityMapper extends SuperMapper {
 
    public Map<Object, Object> find(MapParam param);
    
}

 

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

 

    @Test
    public void testMap() {
       MapParam param = new MapParam("code", "name");
       Map<Object, Object> result = cityMapper.find(param);
       System.out.println(result);
    }

 

说明

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

11
5
分享到:
评论
12 楼 luojinbai 2014-12-25  
我还是第一次来 ,学习了
11 楼 skyyan 2014-04-02  
其实直接 写个ResultHandler实现 就可以
详见 我的地址
http://my.oschina.net/u/1160922/blog
10 楼 40sui 2014-03-28  
今天遇到了这样的问题,看了你的方案茅塞顿开,
9 楼 234390216 2013-11-22  
huxiulei 写道
请问楼主, mybatis系列的文章  接下来还会有更新吗?   还是说告一段落了?

这些文章都是断断续续更新的。什么时候有时间,觉得有什么可以写的时候就会更新。
8 楼 huxiulei 2013-11-22  
请问楼主, mybatis系列的文章  接下来还会有更新吗?   还是说告一段落了?
7 楼 yangjun82101 2013-10-11  
6 楼 234390216 2013-09-17  
非法用户 写道
用mybatis的select(String statement, Object parameter, ResultHandler handler);方法会很简单

道理是一样的。
5 楼 在世界的中心呼喚愛 2013-09-16  
这样的需求很奇怪!!!!!
楼主这种方法不错,通过拦截器拦截符合条件的数据,在进行处理!!
4 楼 非法用户 2013-09-16  
用mybatis的select(String statement, Object parameter, ResultHandler handler);方法会很简单
3 楼 javajiao 2013-09-16  
好文,支持!
2 楼 234390216 2013-09-16  
nichlas_yu 写道
挺好的,学习了。

之前看到有人问这样的问题,刚好昨天有点空,就弄了下,希望对需要的人有帮助。
1 楼 nichlas_yu 2013-09-16  
挺好的,学习了。

相关推荐

    springmybatis

    前面一章,已经搭建好了eclipse,mybatis,mysql的环境,并且实现了一个简单的查询。请注意,这种方式是用SqlSession实例来直接执行已映射的SQL语句: session.selectOne(...

    mybatis基本例子的简单实现

    这个压缩包提供了源代码和一个名为“mybatis映射笔记.odt”的文档,其中详细记录了在实现过程中关于映射遇到的问题和解决方案。 首先,我们要理解MyBatis的核心概念——映射文件(Mapper XML文件)。映射文件是...

    MyBatis Map结果的Key转为驼峰式

    "MyBatis Map结果的Key转为驼峰式" MyBatis 是一个流行的Java持久层框架,它提供了一个简单的方式来访问数据库。然而,在使用MyBatis时,我们经常会遇到一个问题...这是一种非常方便易行的方式,可以满足我们的需求。

    MyBatis-Plus 查询返回实体对象还是map

    这种情况下的查询结果并不是一个完整的实体,而是几个聚合函数的结果,因此返回Map更为合适。 **总结:** 在选择MyBatis-Plus查询返回类型时,应根据实际需求权衡。对于简单的单表查询,返回实体对象通常更合适,...

    Springboot中mybatis表关联映射关系(一对一)

    Springboot 中 MyBatis 表关联...Springboot 中 MyBatis 表关联映射关系是一种强大且灵活的机制,可以实现各种复杂的关联关系。通过使用 `&lt;association&gt;` 元素和相关的属性,我们可以轻松地实现一对一关联关系的映射。

    mybatis分页实现1

    MyBatis 提供了两种主要的分页实现方法:一种是通过在 Mapper XML 文件中直接编写分页 SQL,另一种是利用 MyBatis 的插件机制,如 PageHelper 插件,进行拦截器实现分页。这里我们主要讨论第一种方法,即“普通的...

    Mybatis返回插入主键id的方法

    Mybatis提供了一种便捷的方式,允许我们在插入数据后返回自动生成的主键值。以下将详细介绍如何在Mybatis中实现这一功能。 首先,你需要在Mapper的XML文件中对`&lt;insert&gt;`标签进行配置。关键属性是`useGeneratedKeys...

    Spring+MyBatis多数据源配置实现

    - **分离读写**:一种常见的多数据源策略是主从分离,主数据源处理写操作,从数据源处理读操作,以提高系统的读性能。 - **事务一致性**:确保在涉及多个数据源的操作中,事务管理能够正确处理异常,保证数据的...

    mybatis调用存储过程

    在数据库管理中,存储过程是一种预编译的SQL语句集合...总之,MyBatis调用存储过程提供了一种灵活且高效的方式,使得Java应用能充分利用数据库的高级功能。通过正确配置和使用,开发者可以实现更高效、安全的数据操作。

    Mybatis最新版

    8. 自动类型匹配:Mybatis能自动识别JDBC驱动返回的类型并将其转换为Java类型,同时允许开发者通过`typeHandler`自定义类型处理器。 9. 异常处理:Mybatis有一套完整的异常体系,如`...

    scala + mybatis 数据库查询

    Scala是一种强大的多范式编程语言,它结合了面向对象和函数式编程的特性。MyBatis则是一款流行的Java持久层框架,主要用于简化数据库操作。在本项目中,"scala + mybatis 数据库查询",我们将探讨如何将Scala与...

    MyBatis的resultMap详解

    在 MyBatis 中,我们可以使用两种方式来指定查询结果的返回类型,一种是使用 resultType,另一种是使用 resultMap。其中,resultType 是直接指定返回类型的,而 resultMap 则是对外部 ResultMap 的引用。 当我们...

    mybatis-3.5.9 源码(mybatis-3-mybatis-3.5.9.zip)

    有 SimpleExecutor、ReuseExecutor 和 BatchExecutor 三种实现,分别对应简单执行、重用 Statement 和批量操作的场景。 4. **ParameterHandler 参数处理器** - ParameterHandler 负责将 Java 对象的参数映射到 SQL...

    mybatis-3.0.4源码

    2. SQLSession:代表一次数据库操作的会话,可以执行SQL语句并返回结果。它支持事务管理,提供了insert、update、delete和select等方法。 3. Mapper接口和Mapper XML文件:Mapper接口定义了数据库操作的方法,而...

    mybatis实现增删改查Java项目

    4. **DAO(Data Access Object)设计模式**:DAO 是一种设计模式,用于封装对数据库的所有访问。在本项目中,你会看到如 `UserDao.java` 的接口,其中定义了增删改查的方法。这些方法将被具体的实现类调用,如 `...

    mybatis逻辑分页,含分页导航

    为了提高用户体验,分页是一种常见的优化手段,它允许用户逐步加载和查看大量数据,而不是一次性加载所有内容。MyBatis,作为一款轻量级的Java持久层框架,提供了灵活的SQL映射功能,自然也支持分页查询。本文将深入...

    Jsp+Servlet+MyBatis完成分页查询

    首先,JSP(JavaServer Pages)是一种动态网页技术,允许开发者在HTML页面中嵌入Java代码。在分页查询中,JSP通常用于创建用户界面,展示数据并接收用户的请求参数,如当前页码和每页显示的记录数。 Servlet是Java ...

    MyBatis调用存储过程

    在数据库管理中,存储过程是一种预编译的SQL语句集合,它可以封装一系列复杂的操作,提高数据处理效率,同时提供更好的安全性和可重用性。MyBatis作为一款流行的Java持久层框架,提供了调用存储过程的功能。下面将...

    Mybatis高级映射查询

    3. 参数映射:Mybatis 支持两种参数映射方式,分别是传入 Java 对象和 Map。使用 `@Param` 注解可以为传入的参数命名,便于在 SQL 中引用。对于 Map 参数,键作为参数名,值作为参数值。 4. resultMap:`...

Global site tag (gtag.js) - Google Analytics