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 开源代码项 ...
随机推荐
- Day3:集合
一.集合的定义及特性 1.集合的特性 1.1 去重,把一个列表变成集合,就自动去重了 1.2 关系测试,测试两组数据之间的交集.差集等关系 #!/usr/bin/env python # -* ...
- xml 标准字符过滤
今天在代码里面看见一串非常奇怪的推断语句 if (c < 0x9 || c > 0x9 && c < 0xA || c > 0xA && c & ...
- Summary Day30
1.内存管理 1.1 进程中的内存区域划分 代码区 仅仅读常理区 全局区 BSS 堆 栈 1.2 字符串存储形式之间的比較 字符指针,字符数组.字符动态内存 1.3 虚拟内 ...
- stm32的ADC外设地址设置算法
摘自:https://wenku.baidu.com/view/e3ce2a215901020207409c15.html### /////////////////////////////////// ...
- WPF应用程序启动的问题(自定义Main函数启动)
问题引入: 一般WPF创建之后可以直接运行并不需要编写Main函数指定入口,但是在开发的过程中会遇到一些情况需要自定义Main让WPF从指定的Main函数中进行启动,这样可能会更好控制一点.但是我们再 ...
- bootstrap+fileinput插件实现可预览上传照片功能
实际项目中运用: 功能:实现上传图片,更改上传图片,移除图片的功能 <!DOCTYPE html> <html> <head> <meta charset=& ...
- <p><img src="http://img.blog.csdn.net/20150823142545135?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt=""></p>
/* 实现功能:用顺序表实现栈的各种操作 编译环境:Windows 64b,vc6.0 日期: 2015/7/20 作者:wtt561111 */ #define stack_max_num 10 # ...
- 读Effective Objective-C [提高OC代码质量总结笔记第一篇:熟悉OC]
一.OC特性 OC 为 C 语言添加了面向对象特性,是其超集; OC 使用动态绑定的消息结构,也就是,在运行时才会检查对象类型; 接收一条消息后,究竟应执行何种代码,由运行期环境来决定,而非 编译器; ...
- css 翻牌 翻转 3d翻转 特效
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- C# 使用 RabbitMQ
1. RabbitMQ MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接 ...