mybatis返回map结果集_Mybatis之结果处理器

17af370944492f20b886054312af7da3.png

前言

在上文Mybatis之方法如何映射到XML中讲到需要实例化SqlCommand和MethodSignature两个类,在MethodSignature初始化的时候有一个resultHandlerIndex的参数用来指定是否设置了ResultHandler参数,本文将重点ResultHandler如何使用,分析如何触发的以及如何自定义结果处理器。

使用结果处理器

首先看一下Mybatis提供的结果处理器接口类ResultHandler:

public interface ResultHandler { void handleResult(ResultContext extends T> resultContext);}

还是比较简单的,就只有一个handleResult的方法,方法参数是ResultContext里面存放了正常执行sql的结果,这里的结果处理器其实就是对结果进行二次加工处理;Mybatis提供了两个默认的处理器分别是:DefaultResultHandler和DefaultMapResultHandler,分别处理list和map,下面看一下是如何使用这两个处理器的:

 public static void selectHandler(SqlSession session, Configuration configuration) { BlogMapper mapper = session.getMapper(BlogMapper.class); //DefaultResultHandler内置结果处理器 DefaultResultHandler defaultHandler = new DefaultResultHandler(); mapper.selectBlogsByHandler("zhaohui", defaultHandler); System.out.println(defaultHandler.getResultList()); //DefaultMapResultHandler内置结果处理器 DefaultMapResultHandler defaultMapResultHandler = new DefaultMapResultHandler("id", configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory()); mapper.selectBlogsByHandler("zhaohui", defaultMapResultHandler); System.out.println(defaultMapResultHandler.getMappedResults()); }

上面的实例分别使用了两个内置处理器,分别处理获取到的相同结果;map处理器指定了id作为key;下面再看一下BlogMapper中的selectBlogsByHandler:

47734996940c42757544ff6d44558051.png

上面唯一要注意的点就是selectBlogsByHandler的返回值void类型,不然结果处理器是不会触发处理结果的,这个后面再分析为什么;看一下输出结果:

[Blog [id=159, title=hello java, author=zhaohui, content=hello java666], Blog [id=160, title=hello java, author=zhaohui, content=hello java666]]{160=Blog [id=160, title=hello java, author=zhaohui, content=hello java666], 159=Blog [id=159, title=hello java, author=zhaohui, content=hello java666]}

处理流程

首先我们需要了解结果处理器在什么情况下才会被触发,其次再看什么时候被调用的,最后我们在分析一下内置的处理器什么时候被使用的;

1.触发条件

上一节中提到需要指定void返回类型,主要原因可以查看MapperMethod的execute方法,MapperMethod在Mybatis之方法如何映射到XML中是有介绍的,重点讲到了SqlCommand和MethodSignature类,下面看一下execute方法部分代码:

public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { ...省略... case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; }  ...省略... return result; }

可以看到什么情况下才会调用结果处理器有三个要求
1.首先必须是查询命令,其他的增删改是没有结果处理器的;
2.其次是方法的返回值必须是void,这其实也好理解,如果本身方法就有返回值,结果处理器也有返回值,反而容易搞混;
3.必须要有结果处理器,这个肯定是必须的,没有也别谈结果处理器了。

2.何时调用

具体何时调用我们自己的结果处理器,相关处理主要在DefaultResultSetHandler中,也就是在处理结果映射的时候,更多可以参考Mybatis之XML如何映射到方法,在主方法handleResultSets中调用的handleResultSet方法:

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults, ResultMapping parentMapping) throws SQLException { try { if (parentMapping != null) { handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping); } else { if (resultHandler == null) { DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory); handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null); multipleResults.add(defaultResultHandler.getResultList()); } else { handleRowValues(rsw, resultMap, resultHandler, rowBounds, null); } } } finally { // issue #228 (close resultsets) closeResultSet(rsw.getResultSet()); } }

重点看一下没有指定parentMapping的情况下,如果没有指定resultHandler则系统会创建一个默认的DefaultResultHandler,如果有则用用户自己的处理器;然后就是通过对象工厂创建对象,然后通过类型处理器读取ResultSet,最后调用callResultHandler方法来调用处理器:

 private void callResultHandler(ResultHandler> resultHandler, DefaultResultContext resultContext, Object rowValue) { resultContext.nextResultObject(rowValue); ((ResultHandler) resultHandler).handleResult(resultContext); }

把封装好数据的对象rowValue放入resultContext中,然后传入处理器中进行处理;所以需要注意的是如果有多条记录其实是一条一条传入结果处理器进行处理的,并不是生成一个list然后交给ResultHandler处理,但是内置的Map结果集却不是这样处理的,接下来重点看一下Mybatis内置的两个处理器;

3.内置结果处理器

3.1 DefaultResultHandler
内置了一个ArrayList对象,可以简单的理解为将结果集放入list中;但是上面我们也看到DefaultResultHandler并不是给用户使用的,而是Mybatis自己使用的,在用户没有指定处理器,Mybatis会自己创建一个DefaultResultHandler,而这个处理器是一个局部对象,每次getResultList之后其实还是放在了一个multipleResults中:

public List handleResultSets(Statement stmt) throws SQLException { final List multipleResults = new ArrayList(); ...处理ResultSet...

这里定义的multipleResults是否可以替换成DefaultResultHandler,感觉会更加合理;

3.2 DefaultMapResultHandler
内置了一个HashMap对象,此类也是被Mybatis内部使用在处理结果集是Map时使用,具体代码如下:

public  Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { final List extends V> list = selectList(statement, parameter, rowBounds); final DefaultMapResultHandler mapResultHandler = new DefaultMapResultHandler(mapKey, configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory()); final DefaultResultContext context = new DefaultResultContext(); for (V o : list) { context.nextResultObject(o); mapResultHandler.handleResult(context); } return mapResultHandler.getMappedResults(); }

这里首先获取结果集list,然后遍历结果集通过DefaultMapResultHandler转成Map结果集;这里个人感觉可以直接把DefaultMapResultHandler作为参数传入selectList中,这样最后就无需再次遍历一遍;

自定义结果处理器

自定义一个结果处理器也很简单,实现ResultHandler接口即可,比如下面的Map处理器:

public class MyResultHandler implements ResultHandler { Map result = new HashMap(); @Override public void handleResult(ResultContext extends Blog> resultContext) { Blog blog = resultContext.getResultObject(); System.out.println(blog.toString()); result.put(blog.getId(), blog); } public Map getResult() { return result; }}

简单测试一下,同样查询Blog,如下所示:

 public static void selectMyHandler(SqlSession session) { BlogMapper mapper = session.getMapper(BlogMapper.class); MyResultHandler handler = new MyResultHandler(); mapper.selectBlogsByHandler("zhaohui", handler); System.out.println(handler.getResult()); }

日志输出如下:

Blog [id=159, title=hello java, author=zhaohui, content=hello java666]Blog [id=160, title=hello java, author=zhaohui, content=hello java666]{160=Blog [id=160, title=hello java, author=zhaohui, content=hello java666], 159=Blog [id=159, title=hello java, author=zhaohui, content=hello java666]}

可以发现分别打印了两次Blog,因为每次生成一个Blog对象都会调用一次handleResult;

总结

本文首先介绍了如何使用结果处理器,然后引出什么情况下才能触发处理器需要有三个条件,以及Mybatis内置的两个处理器分别处理list和map,最后自定义了一个简单的结果处理器。

示例代码地址

https://github.com/ksfzhaohui/blog/tree/master/mybatis

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值