您的位置:首页 > 其它

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>

配置好以后就可以使用该拦截器进行拦截了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: