Ibatis之RowHandler
如果一个场景:账户表中有1千万账户,现在,我们需要这个1千万账户利息结算业务。需求是基于Ibatis框架来实现这个功能。
如果按照一般的编程模式,我们将编写一个sql,然后调用QueryForList获得帐户List。然后遍历List逐条对数据进行结息操作,但这样做非常可能会出现性能问题,如:
1.对JVM内存的大量消耗;
2.大量对象的密集创建和销毁对GC带来非常大的负担;
出现性能问题的解决办法在于:QueryForList是把查询结果构造完毕以后才交给结息程序的,这意味着JVM会爆发性的创建一千万个对象到内存中,而且这1千万个对象会在内存中持续非常长时间——由于仅仅有等全部帐户结完息之后这些对象才会失去引用,那么能够判断这些对象也是爆发式的销毁。这对内存的消耗是非常巨大的。
为应对这种问题,Ibatis提供了RowHandler接口,同意程序猿对查询结果进行自己定义的处理,RowHandler接口代码例如以下:
/**
* Event handler for row by row processing.
* <p/>
* The RowHandler interface is used by the SqlMapSession.queryWithRowHandler() method.
* Generally a RowHandler implementation will perform some row-by-row processing logic
* in cases where there are too many rows to efficiently load into memory.
* <p/>
* Example:
* <pre>
* sqlMap.queryWithRowHandler ("findAllEmployees", null, new MyRowHandler()));
* </pre>
*/
public interface RowHandler { /**
* Handles a single row of a result set.
* <p/>
* This method will be called for each row in a result set. For each row the result map
* will be applied to build the value object, which is then passed in as the valueObject
* parameter.
*
* @param valueObject The object representing a single row from the query.
* @see com.ibatis.sqlmap.client.SqlMapSession
*/
void handleRow(Object valueObject); }
DefaultRowHandler是RowHandler接口的默认实现,代码例如以下:
public class DefaultRowHandler implements RowHandler { private List list = new ArrayList(); public void handleRow(Object valueObject) {
list.add(valueObject);
} public List getList() {
return list;
} public void setList(List list) {
this.list = list;
} }
我们调用queryForList方法得到的list便是由该接口提供的。Ibatis构造这个list的过程大致例如以下:当运行完查询sql得到ResultSet之后。会循环遍历这个ResultSet,在每一次循环中调用DefaultRowHandler的handleRow方法,循环结束list也就构造完毕了,然后把list返回。到这里相信大家已经知道RowHandler的本质了,我们仅仅须要定义自己的Handler,然后调用queryWithRowHandler方法,Ibatis运行的就是我们自己定义的handleRow方法了。在handleRow方法里把结息操作做了,就不会出现内存暴增的情况了。由于每次循环结束valueObject便失去了句柄引用。非常快就会被GC清理掉了。
上面通过自己定义RowHandler攻克了内存占用的问题,但还有其它问题,handleRow中的结息操作是同步运行还是异步运行呢?假设是同步运行。那可能会带来新的问题:
首先,同步运行并没有降低总时间,用queryForList和用queryWithRowHander的总时间都为——(数据库端的查询时间+Java端取数据的时间+10000000*结息时间)。
再有,ResultSet并非一次性把1千万条数据返回的,而是在循环遍历的过程中随用随取,比方先取10条,运行到第11次循环的时候再取10条,依此类推,而一次取多少和FetchSize的设置有关系,那么在1千万个账户结息完毕之前数据库连接都无法释放而且数据库端会维持一个Cursor配合ResultSet取数据。这会带来长时间的、非常大的资源开销。
那么。将结息息操作异步化则是更好的方案。我们能够在handlRow方法中把账户对象Add到还有一个或多个线程的结息队列中,那么取数据和结息便能并行运行,这样总时间会少一些(FetchSize不变的情况下缩短的是结息的时间。数据库端的查询时间和Java端取数据的时间是不变的)。但可能不会少太多。这和FethSeize的设置有关系。假设FetchSize比較大,[Java端取数据的时间]会非常短,那并行的时间也会非常短。则短时间内完毕结息的账户数量也不会太多;假设FetchSize比較小,那取数据的时间会较长。并行的时间也会长,则完毕结息的账户数量也会多一些。
大家应该已经看到了,事实上异步化之后。假设FetchSize非常大的话,queryWithRowHandler和queryForList就没什么差别了,由于数据非常快就能取完(我做測试:两千六百多万条记录3分多钟就取完了)。相同会带来内存的暴增。而FetchSize太小的话又会导致Java和数据库之间频繁的网络IO,一样影响性能。所以FetchSize的设置就非常关键,太大太小都不行,须要考虑[取数据]的速率、[结息操作]的速率和内存大小三方面的要素。
上面说了这么多,都是偏理论性的东西,一切都需依据自己的实际情况进行抉择。
* 假设内存够大,queryForList一样非常OK;
* 假设内存是瓶颈,能够改用queryWithRowHandler,假设连接池不紧张。数据库端没什么资源压力,同步handle一样OK。
* 假设还有优化的需求,则能够考虑异步handle,但是这一次FetchSize设置需要仔细权衡,这不是太大,也不能太小。
版权声明:本文博主原创文章,博客,未经同意不得转载。
Ibatis之RowHandler的更多相关文章
- ibatis
ibatis学习笔记(一)>>>>>>>sqlMapConfig.xml文件详解 1.sqlMapConfig.xml配置文件详解: Xml代码 1. < ...
- 【转】Mybatis/Ibatis,数据库操作的返回值
该问题,我百度了下,根本没发现什么有价值的文章:还是看源代码(详见最后附录)中的注释,最有效了!insert,返回值是:新插入行的主键(primary key):需要包含<selectKey&g ...
- 【转】 Mybatis/Ibatis,数据库操作的返回值
该问题,我百度了下,根本没发现什么有价值的文章:还是看源代码(详见最后附录)中的注释,最有效了!insert,返回值是:新插入行的主键(primary key):需要包含<selectKey&g ...
- ibatis框架的sqlmapclient接口
SqlMapClient,是iBatis中的重要接口,这个接口涉及到对SQL映射的执行和批处理. 现在,就先了解它的对query开头方法的定义. 首先是 queryForList 方法: //指定SQ ...
- iBatis系列一
XML iBatis可以使用xml来作为参数输入以及结果返回:这个功能的优势在于某些特定的场景:还有可以通过DOM方式来作为参数传递:但是这个方式应用的比较少,如果服务器是xml服务器可以采用这种方式 ...
- Ibatis教程
Ibatis教程 |字号 转自:http://blog.csdn.net/lhminjava/article/details/1871136 ibatis 开发指南ibatis Quick S ...
- ibatis源码学习4_参数和结果的映射原理
问题在详细介绍ibatis参数和结果映射原理之前,让我们先来思考几个问题.1. 为什么需要参数和结果的映射?相对于全自动的orm,ibatis一个重要目标是,通过维护POJO与SQL之间的映射关系,让 ...
- ibatis源码学习1_整体设计和核心流程
背景介绍ibatis实现之前,先来看一段jdbc代码: Class.forName("com.mysql.jdbc.Driver"); String url = "jdb ...
- iBatis --> MyBatis
从 Clinton Begin 到 Google(从 iBatis 到 MyBatis,从 Apache Software Foundation 到 Google Code),Apache 开源代码项 ...
随机推荐
- 希捷硬盘扩容软件-----DiscWizard
SeagateDiscWizard可为Seagate磁盘驱动器的使用提供便利.DiscWizard可帮助您迅速安装新的磁盘驱动器.并通过安装向导指导您在磁盘驱动器上完毕分区的创建和格式化. DiscW ...
- 短网址ShortUrl的算法
场景: 我们在新浪微博上公布网址的时候.微博会自己主动判别网址.并将其转换.比如:http://t.cn/hrYnr0. 为什么要这样做的,原因我想有这样几点: 1.微博限制字数为140字一条,那么假 ...
- cpu信息
在linux系统下能够通过cat /proc/cpuinfo来查看本机上cpu的相关信息,通过processor能够推断逻辑cpu的个数,physical id能够推断物理cpu的个数,通过cpu c ...
- linux中内存泄漏的检測(一)最简单的方法
什么是内存泄漏 内存泄漏是指程序动态申请的内存在使用完后没有释放,导致这段内存不能被操作系统回收再利用. 比如这段程序,申请了4个字节的空间但没有释放,有4个字节的内存泄漏. #include < ...
- POJ3171 Cleaning Shifts DP,区间覆盖最值
题目大意.N个区间覆盖[T1,T2]及相应的代价S,求从区间M到E的所有覆盖的最小代价是多少. (1 <= N <= 10,000).(0 <= M <= E <= 86 ...
- Java中使用org.json和json-lib解析JSON
文章目录 [隐藏] 一.JavaProject中org.json解析JSON 1.JSON的org.son-api下载 1)JSON网址 2)JSON的java解析org.json-api网址 3) ...
- wikioi 1051哈希表
题目描写叙述 Description 给出了N个单词,已经按长度排好了序.假设某单词i是某单词j的前缀,i->j算一次接龙(两个同样的单词不能算接龙). 你的任务是:对于输入的单词,找出最长的龙 ...
- 关于stm32的RAM大小
- [React] Cleanly Map Over A Stateless Functional Component with a Higher Order Component
In this lesson we'll create a Higher Order Component (HOC) that takes care of the key property that ...
- GoJS超详细入门(插件使用无非:引包、初始化、配参数(json)、引数据(json)四步)
GoJS超详细入门(插件使用无非:引包.初始化.配参数(json).引数据(json)四步) 一.总结 一句话总结:插件使用无非:引包.初始化.配参数(json).引数据(json)四步. 1.goj ...