mybatis返回map实现
2014-02-18 09:48
316 查看
一、前言
有时候我们在进行Mybatis系统开发的时候,会遇到这样一些需求:期望查询一个表返回的结果是一个map,map的key表示表的一个字段,value则又另一个表的字段表示;比如:一个性别sex,一个对应人数number,其中sex为key,number为value。实现这样的需求有两种方式,一种是静态实现,对应的另一种是动态拦截实现。
二、实现
1、静态实现:指定查询 Mapper 语句的 resultType 为 map 时,返回的结果是一个 Map 列表;比如:
mapper.xml:
<resultMap id="map">
<result property="sex" column="sex"/>
<result property="number" column="number"/>
</resultMap>
<select id="selectSex" resultClass="map">
select sex,count(*) as number from person
</select>
java:
/**
* 对某个学校计算校友
*
* @param school
*/
public void countFrends(School school) {
Map<String, HashMap<String, Long>> mapSex = sqlSession .selectMap(SchoolConst.SELECT_SEX, school, SchoolConst.KEY);
if (Utils.checkNotNullAndUnempty(mapSex)) {
if (Utils.checkNotNullAndUnempty(mapSex.get(FEMALE))) {
school.setFemale(mapSex.get(FEMALE).get(SchoolConst.VALUE).intValue());
{color:#000000}}
if (Utils.checkNotNullAndUnempty(mapSex.get(MALE))) {
school.setMale(mapSex.get(MALE).get(SchoolConst.VALUE).intValue());
{color:#000000}}
}
}
mapsex打印效果:{0={sex=0, number=25}, 1={sex=1, number=4}}
很显然查询到的结果格式并不是我们真正想要。原因其实就是mapper先根据条件查询出一个List集合,然后遍历List集合,将对象以Key,Value(为查到的行记录)的形式存入Map中返回。那怎么样可以让 Mybatis
查询出来的结果是一个 Key 为表中某一列的值, Value 为表中另一列的值的 Map 呢?下面介绍一种利用拦截器进行动态拦截返回map的方法。
2、动态拦截实现
java实现的mysql执行一般过程:
(1) 调用Class.forName()方法加载驱动程序。
(2) 调用DriverManager对象的getConnection()方法,获得一个Connection对象。
(3) 创建一个Statement对象,准备一个SQL语句,这个SQL语句可以是Statement对象(立即执行的的语句)、PreparedStatement语句(预编译的语句)或CallableStatement对象(存储过程调用的语句)。
(4) 调用excuteQuery()等方法执行SQL语句,并将结果保存在ResultSet对象;或者调用executeUpdate()等方法执行SQL语句,不返回ResultSet对象的结果。
(5)对返回的ResultSet对象进行显示等相当的处理。
(6)释放资源。
类似的在spring+mybatis中:
----------------------------------------------
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
{color:#000000}} finally {{color}
closeStatement(stmt);
{color:#000000}}
}
-------------------------------------------
public <E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleResultSets(statement);
{color:#000000}}
从上面两段代码可以知道返回结果最终是由 ResultSetHandler 的 handleResultSets 方法对当前的 Statement 处理后的结果,所以我们如果要改变返回结果的话就可以使用 Mybatis 的拦截器对 ResultSetHandler 接口的 handleResultSets 方法进行拦截。
拦截条件我们可以通过 ParameterObject 来进行的。当某一个语句需要返回一个 Map 时,就可以进行拦截处理。
接下来就是怎么来实现拦截器了。我们定义一个 拦截器KeyValueInterceptor用于拦截结果集并返回一个map。
@Intercepts(@Signature(method="handleResultSets", type=ResultSetHandler.class, args={Statement.class}))
public class KeyValueInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
//获取代理的目标对象
Object target = invocation.getTarget();
if (target instanceof FastResultSetHandler){
FastResultSetHandler resultSetHandler = (FastResultSetHandler) target;
//利用反射机制获取ParameterHandler属性,再获取到ParameterObject;
ParameterHandler parameterHandler = ReflectUtil.getFieldValue(resultSetHandler, "parameterHandler");
Object parameterObj = parameterHandler.getParameterObject();
//判断ParameterObj是否是HashMap
if (parameterObj instanceof HashMap) {
HashMap resultMap = (HashMap) parameterObj;
Statement stmt = (Statement) invocation.getArgs()[0];
//通过Statement获取到当前的结果集,并返回处理结果
return processResultSet(stmt.getResultSet(), resultMap);
}
}
return invocation.proceed();
}
/**
* 处理结果集
*/
private Object processResultSet(ResultSet resultSet, HashMap resultMap) {
if (resultSet != null) {
try {
//把每行对应的Key和Value存放到Map中
while (resultSet.next()) {
Object key = resultSet.getObject("sex");
Object value = resultSet.getObject("number");
resultMap.put(key, value);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
}
}
List<Object> resultList = new ArrayList<Object>();
resultList.add(resultMap);
return resultList;
}
return null;
}
public Object plugin(Object obj) {
return Plugin.wrap(obj, this);
}
public void setProperties(Properties props) {
}
{color:#000000}}
反射工具类:
public class ReflectUtil {
/**
* 利用反射获取指定对象的指定属性
*/
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) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return (T)result;
}
/**
* 利用反射获取指定对象里面的指定属性
*/
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) {
}
}
return field;
}
{color:#000000}}
插件配置:
<plugins>
<plugin interceptor="com.qunar.testbatis.KeyValueInterceptor"/>
</plugins>
配置好以后就可以使用该拦截器进行拦截了。
有时候我们在进行Mybatis系统开发的时候,会遇到这样一些需求:期望查询一个表返回的结果是一个map,map的key表示表的一个字段,value则又另一个表的字段表示;比如:一个性别sex,一个对应人数number,其中sex为key,number为value。实现这样的需求有两种方式,一种是静态实现,对应的另一种是动态拦截实现。
二、实现
1、静态实现:指定查询 Mapper 语句的 resultType 为 map 时,返回的结果是一个 Map 列表;比如:
mapper.xml:
<resultMap id="map">
<result property="sex" column="sex"/>
<result property="number" column="number"/>
</resultMap>
<select id="selectSex" resultClass="map">
select sex,count(*) as number from person
</select>
java:
/**
* 对某个学校计算校友
*
* @param school
*/
public void countFrends(School school) {
Map<String, HashMap<String, Long>> mapSex = sqlSession .selectMap(SchoolConst.SELECT_SEX, school, SchoolConst.KEY);
if (Utils.checkNotNullAndUnempty(mapSex)) {
if (Utils.checkNotNullAndUnempty(mapSex.get(FEMALE))) {
school.setFemale(mapSex.get(FEMALE).get(SchoolConst.VALUE).intValue());
{color:#000000}}
if (Utils.checkNotNullAndUnempty(mapSex.get(MALE))) {
school.setMale(mapSex.get(MALE).get(SchoolConst.VALUE).intValue());
{color:#000000}}
}
}
mapsex打印效果:{0={sex=0, number=25}, 1={sex=1, number=4}}
很显然查询到的结果格式并不是我们真正想要。原因其实就是mapper先根据条件查询出一个List集合,然后遍历List集合,将对象以Key,Value(为查到的行记录)的形式存入Map中返回。那怎么样可以让 Mybatis
查询出来的结果是一个 Key 为表中某一列的值, Value 为表中另一列的值的 Map 呢?下面介绍一种利用拦截器进行动态拦截返回map的方法。
2、动态拦截实现
java实现的mysql执行一般过程:
(1) 调用Class.forName()方法加载驱动程序。
(2) 调用DriverManager对象的getConnection()方法,获得一个Connection对象。
(3) 创建一个Statement对象,准备一个SQL语句,这个SQL语句可以是Statement对象(立即执行的的语句)、PreparedStatement语句(预编译的语句)或CallableStatement对象(存储过程调用的语句)。
(4) 调用excuteQuery()等方法执行SQL语句,并将结果保存在ResultSet对象;或者调用executeUpdate()等方法执行SQL语句,不返回ResultSet对象的结果。
(5)对返回的ResultSet对象进行显示等相当的处理。
(6)释放资源。
类似的在spring+mybatis中:
----------------------------------------------
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
{color:#000000}} finally {{color}
closeStatement(stmt);
{color:#000000}}
}
-------------------------------------------
public <E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleResultSets(statement);
{color:#000000}}
从上面两段代码可以知道返回结果最终是由 ResultSetHandler 的 handleResultSets 方法对当前的 Statement 处理后的结果,所以我们如果要改变返回结果的话就可以使用 Mybatis 的拦截器对 ResultSetHandler 接口的 handleResultSets 方法进行拦截。
拦截条件我们可以通过 ParameterObject 来进行的。当某一个语句需要返回一个 Map 时,就可以进行拦截处理。
接下来就是怎么来实现拦截器了。我们定义一个 拦截器KeyValueInterceptor用于拦截结果集并返回一个map。
@Intercepts(@Signature(method="handleResultSets", type=ResultSetHandler.class, args={Statement.class}))
public class KeyValueInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
//获取代理的目标对象
Object target = invocation.getTarget();
if (target instanceof FastResultSetHandler){
FastResultSetHandler resultSetHandler = (FastResultSetHandler) target;
//利用反射机制获取ParameterHandler属性,再获取到ParameterObject;
ParameterHandler parameterHandler = ReflectUtil.getFieldValue(resultSetHandler, "parameterHandler");
Object parameterObj = parameterHandler.getParameterObject();
//判断ParameterObj是否是HashMap
if (parameterObj instanceof HashMap) {
HashMap resultMap = (HashMap) parameterObj;
Statement stmt = (Statement) invocation.getArgs()[0];
//通过Statement获取到当前的结果集,并返回处理结果
return processResultSet(stmt.getResultSet(), resultMap);
}
}
return invocation.proceed();
}
/**
* 处理结果集
*/
private Object processResultSet(ResultSet resultSet, HashMap resultMap) {
if (resultSet != null) {
try {
//把每行对应的Key和Value存放到Map中
while (resultSet.next()) {
Object key = resultSet.getObject("sex");
Object value = resultSet.getObject("number");
resultMap.put(key, value);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
}
}
List<Object> resultList = new ArrayList<Object>();
resultList.add(resultMap);
return resultList;
}
return null;
}
public Object plugin(Object obj) {
return Plugin.wrap(obj, this);
}
public void setProperties(Properties props) {
}
{color:#000000}}
反射工具类:
public class ReflectUtil {
/**
* 利用反射获取指定对象的指定属性
*/
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) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return (T)result;
}
/**
* 利用反射获取指定对象里面的指定属性
*/
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) {
}
}
return field;
}
{color:#000000}}
插件配置:
<plugins>
<plugin interceptor="com.qunar.testbatis.KeyValueInterceptor"/>
</plugins>
配置好以后就可以使用该拦截器进行拦截了。
相关文章推荐
- Mybatis返回Map的一种实现
- Mybatis返回Map的一种实现
- Mybatis返回Map的一种实现
- Mybatis返回Map的一种实现
- Mybatis返回Map的一种实现
- MyBatis查询两个字段,返回Map,一个字段作为key,一个字段作为value的实现
- Mybatis返回Map的一种实现
- Mybatis返回Map的一种实现
- MyBatis实现单表增删改查(CURD)--查询所有返回Map
- Mybatis返回Map的一种实现
- mybaties配置多个resultMap,实现返回不同的实体
- mybatis 返回Map的key大小写问题
- 使用mybatis自动实现接口封装返回结果集
- mybatis返回map类型数据空值字段不显示
- MyBatis之使用resultMap实现高级映射
- mybatis 查询 resultMap="" 只返回一条数据
- mybatis使用resultMap实现多对多查询 (需求:商品信息和订单明细有多对多的关系)
- mybatis返回map类型数据空值字段不显示
- Mybatis 如何 返回 List<String> 类型 或 List<Map<String,Object>>类型
- Spring mvc mybatis 中Date类型 在返回给前端时需要格式化的实现方式