关于文章“查询数据库后是返回ResultSet还是返回Collection”
http://www.jdon.com:81/jive/article.jsp?forum=16&thread=302

我看了你的这篇文章,感觉非常气愤,而且竟然还有这么多人附和, 真是误导!

按照传统返回Collection的方法, 除了内存多开销一点之外, 在速度上会比你每次都iterator.next()再去访问rs.next()要快的多! 而按照你的方法, 除了节约一点内存之外, 没有任何效率上的优势, 而且白白浪费数据库连接。

最后,假如按照这种方法去访问实体Bean的话,将极有可能导致极其低下的效率 -- 大量的网络吞吐和大量的事务产生!

因此,看了你的文章, 实在感觉气愤!

Hi Nethero, Bang, nice to meet you. I am very new to jdon forum, but like it, compare to other sites( I just started surfing since last week )the discussions here are professional, people seems more serious, so I like it.
How to return the search result from DB is a very big topic, both of your posts make sense under certain scenario:

1> To process tons of records sequentially, e.g batch process, it definitely make sense to have a Collection wrapp around resulset, so you can do incremental fetch, otherwise you may get "Out of memory" and cause too much Garbage collection due to memory spike.

2> For presentation, typically data volume for individual query is not very high, users are more care about response time, then a read-ahead( as Nethero pointed out) bring the better result. Anothe advantage of this approach is it makes your front end less location sensitive, think about when your jsp is running in a seperate servlet VM and your data access is in AppServer.

3> One step further, to copy it to a light weighted collection, you can even close the resultset after each fetch and reopen it upon next fetch request. it may save huge memory and other DB resources( DB connections, cursors ), especialy useful under heavy load and you have no control the size of user query.
Of course, scrollable resultset or a smart exlusion cursor has to be used to archive this.

My type in Chinese is very slow, hope using English( I am not comfortable neither) make no offend to anybody here.

-Wanchun

我对浆糊和各位的话有所感触:
浆糊所说的话不错
用Banq所说的Iterator返回的确有这个问题,那篇Bank讲的文章我也去看了。本文中的例子就是那里的。
jive里面采用的longList我看是个类似索引的东西,开始它并不返回所有的全部数据,而在以后需要的时候去取数据,无论是从database还是cache,这样这里以较小的代价来解决这个问题(根据的是jive的运行环境,如果是涉及EJB等或许有更好的办法)。
更正Bank->Banq
突然发现这么多回复,
我可真傻,原来一直没有往下看。。。
呵呵,这篇题目正好记录了大家的进步。也没什么不好的。

iterator就是一个接口,没什么大不了的,它可以降低耦合,足够了。最大的优点。这样带来得直接好处是在表示层调用时大家可以写得更一致一点,代码耦合度低。

jive中的iterater因为掺杂了其他的东西,所以比较好玩一点。
第一,它使用了一个数组记录对象id在内存中,而不是整个对象本身,这样是真正的节约了内存,但是注意,它早早地释放了数据库连接,不会带来问题。
第二,每次取对象时next()是新建数据库连接,重新获取一个对象。详细参见源代码吧。

第三,由于第二会产生效率问题,即使在有连接池的情况下,所以它加入了一个cache类进行缓存。

由于第一点的原因,它的构造函数写得比较复杂了一点,如果返回实际对象,同样也使用iterator(只是简单包装一下),这里可以写得更简单。
建议先不要看2.x的源码,先看1.2.x的比较好理解,2.x的iterator对1.2.x做了重新整理。合并了:)

结论:
如果经常处理大对象(有类似text字段的),那么jive的方法很好。
如果只是一般性的字段,可以使用collections,但是建议增加一个辅助的iterator。

也算学习心得吧,请各位指教。

引用jdk1.4 api doc

java.sql.Statement.close():

When a Statement object is closed, its current ResultSet object, if one exists, is also closed

除了应用要求非常高性能和速度
不建议使用程序级别使用精巧的方法而耗费过多的精力

有时间和兴趣者例外

如果要求速度,换一个JRE。有比sun jdk快得多的

以及技术架构上考虑优化
对于数据库连接关闭后结果集不能在操作,即使关闭了Statement,结果集就不可以使用了(sybase)而在使用ejb的情况下(bmp)这些东西一定随用随关,如果可以关闭后赋空值等待回收,请问究竟是怎样处理的?可否回答以下?
大家谈了很多,我觉得这个东西比较难以作出判断(因为我们都没有办法做性能测试),所以我们应该以大公司的通常使用方式作为参考(他们为了推销自己的产品,绞尽了脑汁提高性能,这个地方他不会不注意的,因为这里是很耗时间的一个环节)

bea的petstore中就使用了arrayList,我想我们完全可以采用这种数据传递方式:
Connection connection = getDataSource().getConnection();
PreparedStatement preparedstatement = connection.prepareStatement("select COUNT(*) from (category a join category_details b on a.catid=b.catid) where locale = ?", 1004, 1007);
preparedstatement.setString(1, locale.toString());
ResultSet resultset = preparedstatement.executeQuery();
resultset.first();
int k = resultset.getInt(1);
resultset.close();
preparedstatement.close();
preparedstatement = connection.prepareStatement("select a.catid, name, descn from (category a join category_details b on a.catid=b.catid) where locale = ? order by name", 1004, 1007);
preparedstatement.setString(1, locale.toString());
resultset = preparedstatement.executeQuery();
Page page;
if(i >= 0 && i < k)
{
ArrayList arraylist = new ArrayList();
resultset.absolute(i + 1);
do
arraylist.add(new Category(resultset.getString(1).trim(), resultset.getString(2), resultset.getString(3)));
while(resultset.next() && --j > 0);
page = new Page(arraylist, i, k);
} else
{
page = Page.EMPTY_PAGE;
}
resultset.close();
preparedstatement.close();
connection.close();
其中的page类的定义如下:
// Decompiled by Jad v1.5.7g. Copyright 2000 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/SiliconValley/Bridge/8617/jad.html
// Update: 李颖聪 e-mail: liyingcong@163.net QQ: 245556
// Source File Name: Page.java

package com.sun.j2ee.blueprints.catalog.model;

import java.io.Serializable;
import java.util.*;

public class Page
implements Serializable
{

public static final Page EMPTY_PAGE;
List objects;
int start;
int containingListSize;

public Page(List list, int i, int j)
{
objects = new ArrayList(list);
start = i;
containingListSize = j;
}

public List getList()
{
return objects;
}

public int getSize()
{
return objects.size();
}

public int getStartOfNextPage()
{
return start + objects.size();
}

public int getStartOfPreviousPage()
{
return Math.max(start - objects.size(), 0);
}

public boolean hasNextPage()
{
return start + objects.size() < containingListSize;
}

public boolean hasPreviousPage()
{
return start > 0;
}

static
{
EMPTY_PAGE = new Page(Collections.EMPTY_LIST, 0, 0);
}
}

看了这么多大师的讨论,最近写了一个将ResultSet转换为Object ArrayList的类,为开源贡献一点微薄之力。
import java.util.*;
import java.sql.*;
import java.io.*;
import java.sql.Timestamp;
import java.text.*;
import java.lang.*;
import java.lang.reflect.*;
public class rsToObj{
private ArrayList getObjectArrayList(Class objectClass,ResultSet rs) throws SQLException
{
Class cl=c;
Field [] fields=cl.getDeclaredFields();
Method[] methods=cl.getDeclaredMethods();
String fieldName="";
String methodName="";
Object[] arguments=null;
ArrayList array=new ArrayList();
while (rs.next()){
try{
Object w=cl.newInstance() ;
for(int f=0;f fieldName=fields[f].getName();
int colum=0;
try{
colum=rs.findColumn(fieldName);
}catch(SQLException e){
continue;
}
for (int m=0;m {
methodName=methods[m].getName();
if(methodName.compareToIgnoreCase("set"+fieldName)==0){
if (fields[f].getType().getName().equals("java.lang.String")){
arguments = new Object[] {rs.getString(colum)};
}
else if(fields[f].getType().getName().equals("int")){
arguments = new Object[] {new Integer(rs.getInt(colum))};
}
else if(fields[f].getType().getName().equals("float")){
arguments = new Object[] {new Float(rs.getFloat(colum))};
}
else if(fields[f].getType().getName().equals("long")){
arguments = new Object[] {new Long(rs.getLong(colum))};
}
else if(fields[f].getType().getName().equals("short")){
arguments = new Object[] {new Short(rs.getShort(colum))};
}
else if(fields[f].getType().getName().equals("double")){
arguments = new Object[] {new Double(rs.getDouble(colum))};
}
else if(fields[f].getType().getName().equals("java.sql.Timestamp")){
arguments = new Object[] {rs.getTimestamp(colum)};
}
try{
methods[m].invoke(w,arguments);
}
catch (InvocationTargetException e) {System.out.println(e);}

break;
}

}

}
array.add(w);
}catch(InstantiationException e){}
catch(IllegalAccessException e){}

}
return array;
}

}

我的几点意见:

1.Iterator是界面,而不是实现。
Iterator只是为我们带来了统一的遍历集合的界面,它本身无法带来性能上的提高。

2.Java2的类集框架(Collection)使用的Iterator与我们使用的Iterator之间的区别.
JDK提供的Iterator为我们提供了基本数据结构(set,map,list等)的统一的遍历界面,当我们要为我们自己的数据结构提供Iterator时我们有两种选择:用基本的数据结构组合成我们的数据结构,使用JDK的Iterator进行遍历(一般的做法);为我们自己的数据结构提供Iterator实现。

3.性能问题.
Iterator是建立在某种数据结构和存取方式基础上的一种遍历算法,性能问题的产生不在Iterator,而在数据结构和存取方式的选择。以大家现在讨论的应用为例,争论的焦点在于是一次取出全部数据还是分批取出数据。
ArrayList的做法是一次取出全部数据保存在服务器的内存中,这样的数据结构和存取方式下的Iterator效率自然高,网络流量也能控制。代价是内存的使用量大。
banq所说的做法,是分批从数据库中取数据,每次只取所需的,这样内存和网络流量都得到控制,代价就是长期连接数据库,所以需要做一个缓冲以减少和数据库的连接次数。EJB环境下还会产上大量的小事务。

4.性能问题的解决之道。
针对项目实际。若是硬件够好,内存够多,可用第一种,非但速度快,而且容易实现与调整。若是内存不够,那只好用程序技巧来达到同样的效果,这也是jive的目的。
我自己的方式则是模仿EJB,返回的是主键,真正数据的读取延迟到具体的实体对象,这样无论是内存和网络流量都得到控制,也不会长期占用连接,但代价就是频繁的连接数据库,性能的瓶颈转到连接池上。当然这是从我的实际出发的,不可能通用。

一点浅见,愿与大家共同探讨。

lsj 总结得很好!!!

没有完美解决之道,根据不同的应用选择不同方法!!!

看了那么多的文章,我都头晕了。不知道到底如何做才好。