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 开源代码项 ...
随机推荐
- Loadrunner--参数化知识点及参数池策略
一.为何进行脚本参数化? 脚本在录制的时候,记录的参数都是常量值,这样,虚拟用户在执行同一个脚本,向服务器发送请求时,使用的都是同一个参数值,与实际不符.所以使用参数化技术. 二.参数化的逻辑? 对脚 ...
- Codeforces Round #258 (Div. 2)——B. Sort the Array
B. Sort the Array time limit per test 1 second memory limit per test 256 megabytes input standard in ...
- fatfs的设置
官方网址:file:///E:/%E5%8D%95%E7%89%87%E6%9C%BA/FATFS/ff13a/documents/doc/config.html 关于多个文件同时打开的配置::在ff ...
- jQuery常用方法(持续更新) jQuery(转)
0.常用代码: 请容许我在1之前插入一个0,我觉得我有必要把最常用的代码放在第一位,毕竟大部分时间大家都是找代码的. (1)AJAX请求 $(function() { $('#send').click ...
- Java中compareTo()方法比较字符串详解
中心:String 是字符串,它的比较用compareTo方法,它从第一位开始比较, 如果遇到不同的字符,则马上返回这两个字符的ascii值差值.返回值是int类型 1.当两个比较的字符串是英文且长度 ...
- gdb查看线程堆栈信息
查看堆栈:gdb -quiet -batch -ex='thread apply all bt' -p pid查看运行位置:gdb -quiet -batch -ex='thread apply al ...
- java用volatile或AtomicBoolean实现高效并发处理 (只初始化一次的功能要求)
最近碰到一个这样的功能要求:怎么在一个类里面,实现高效并发处理下只可以初始化一次的方法? 实现方式: 1)volatile方式: /** * Created by Chengrui on 2015/7 ...
- dmalloc在嵌入式的开发板上的应用
下面是我实际在开发环境里面做的dmalloc移植时候的一些随笔 配置PC的dmalloc环境1. 首先把源码包打开,进入dmalloc文件夹2. ./configure 配置Makefile,我是加了 ...
- 域名从www跳转到非www,Apache和Nginx2种解决方式
背景:www跳转到非www. http://www.jiutianniao.com和http://jiutianniao.com 都可以访问. 但是,想把www这个重定向到非www,输入更简单,让搜 ...
- Linux中LVM2原理及制作LVM2
Linux中LVM2原理及制作LVM2 一.LVM原理 [MD]:Multi Device 多设备 Mdadm是一个用户空间工具,是RAID的管理工具,与真正的RAID工作没有太大关系.真正的RAID ...