既然我們把資料庫訪問封裝起來了,那麼如果查詢資料庫返回的是一系列結果,比如我們從資料庫中得到所有人的使用者名稱,然後在Jsp頁面顯示出來。
這裡就有一個普遍疑問,我這個javabean是返回ResultSet到Jsp中還是Collection?
我曾經有段時間圖省事,直接返回ResultSet,然後在我的jsp頁面中是大量的ResultSet遍曆。這其實還是將資料層和顯示層混淆在一起。在EJB CMP中,返回的是Collection,這樣偶合性降低,不用在修改資料庫結構後,一直修改程式到前台Jsp頁面,這和以前的php Asp開發方式沒兩樣。
但是返回Collection效率不是很高,因為意味著在記憶體中要開闢一個記憶體存放所有的結果。
我看了http://builder.com.com/article.jhtml?id=u00220020814R4B01.htm這篇文章後,覺得啟發很大,返回Iterator就可以了。
Iterator也是個模式,在Jive中大量使用了Iterator,我以前很奇怪,為什麼他沒事自己寫個Iterator,現在知道原因了,這樣節省記憶體,而且效率高。
看下面比較:
public List getUsers() {
ResultSet rs = userDbQuery();
List retval = new ArrayList();
while (rs.next()) {
retval.add(rs.getString(1));
}
return retval;
}
上面是個我們採取返回Collection後最常用的方法,將ResultSet中的使用者名稱加入List再返回,顯然這很耗費記憶體。
使用Iterator返回:
public Iterator getUsers() {
final ResultSet rs = userDbQuery();
return new Iterator() {
private Object next;
public boolean hasNext() {
if (next == null) {
if (! rs.next()) {
return false;
}
next = rs.getString(1);
}
return true;
}
public Object next() {
if (! hasNext()) {
throw new NoSuchElementException();
}
String retval = next;
next = null;
return retval;
}
public void remove() {
throw new UnsupportedOperationException("no remove allowed");
}
}
}
這裡返回的是一個內部類,其實你可以象Jive那樣,專門做個Iterator類,這樣,這裡寫得就不那麼難看,你自己定義的Iterator和Collection中的Iterator沒有任何關係,自己定義了三個方法 hasNext(); next(); remove();這樣看上去和Collection的Iterator是一樣的。
從自己作的這個Iterator類中看到,這個Javabean只是做了一個指標傳遞作用,將調用本Javabean的指標傳遞到ResultSet,這樣既提高了效率,節約了記憶體,又降低了偶合性,這是堪稱中介軟體典型的示範。
既然返回iterator這麼好,有人經常用到一個簡單的返回Iterator方法:
public Iterator getUsers() {
ResultSet rs = userDbQuery();
List list = new ArrayList()
while (rs.next()) {
list.add(rs.getString(1));
}
return list.iterator();
}
這其實和直接返回list沒區別,還是浪費記憶體。
就此篇文章引起爭論:
1.關閉資料庫連接rs是否還能使用?
http://dev.csdn.net/develop/article/17/17705.shtm
如下:
在Connection上調用close方法會關閉Statement和ResultSet嗎?
級聯的關閉這聽起來好像很有道理,而且在很多地方這樣做也是正確的,通常這樣寫
Connection con = getConnection();//getConnection is your method
PreparedStatement ps = con.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
……
///rs.close();
///ps.close();
con.close(); // NO!
這樣做的問題在於Connection是個介面,它的close實現可能是多種多樣的。在普通情況下,你用DriverManager.getConnection()得到一個Connection執行個體,調用它的close方法會關閉Statement和ResultSet。但是在很多時候,你需要使用資料庫連接池,在串連池中的得到的Connection上調用close方法的時候,Connection可能並沒有被釋放,而是回到了串連池中。它以後可能被其它代碼取出來用。如果沒有釋放Statement和ResultSet,那麼在Connection上沒有關閉的Statement和ResultSet可能會越來越多,那麼……
相反,我看到過這樣的說法,有人把Connection關閉了,卻繼續使用ResultSet,認為這樣是可以的,引發了激烈的討論,到底是怎麼回事就不用我多說了吧。(作者意思是:rs的資源沒有釋放,還用的是串連池中的conn)
所以我們必須很小心的釋放資料庫資源,下面的代碼片斷展示了這個過程
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = getConnection();//getConnection is your method
ps = con.prepareStatement(sql);
rs = ps.executeQuery();
///...........
}
catch (SQLException ex) {
///錯誤處理
}
finally{
try {
if(ps!=null)
ps.close();
}
catch (SQLException ex) {
///錯誤處理
}
try{
if(con!=null)
con.close();
}
catch (SQLException ex) {
///錯誤處理
}
}
很麻煩是不是?但為了寫出健壯的程式,這些處理是必須的。
大意如下:
如果不使用串連池機制, 關閉connection, 必然關閉resultset!
如果使用串連池, 所謂的關閉connection, 其實是將串連返回給了串連池,
連線物件依然存在, 所以這實際上不叫關閉!
2.效能問題
如果不是為了模式,用resultset遍曆是最快的,比放進Collection在遍曆要快很多,裝進Collection不也是resultset的遍曆嗎。在jsp中遍曆resultset佔用資料庫資源的說法是不對的。jsp的後台實現就是servlet。為了模式效能上是要付出代價的。
要是你在Collection裡面依然採用直接封裝ResultSet的方式,在效能上沒有絲毫好處。唯一的好處是對jsp程式員屏蔽了RS的訪問。
Collection需要對java.sql.ResultSet的資料進行page的封裝。才有效能上的效果。