谈谈设计模式中的Iterator迭代器
发表于:2007-07-04来源:作者:点击数:
标签:
在阅读本文之前,你需要了解Pet Store的J2EE模式 (http://java.sun.com/blueprints/patterns/catalog.html) 在Pet Store中的CatalogDao使用了DAO模式,从而完成Fast-Lane Reader模式,以便能快速的输出数据库元素 列表,同时使用for page-by-page iteration完
在阅读本文之前,你需要了解Pet Store的J2EE模式
(http://java.sun.com/blueprints/patterns/catalog.html)
在Pet Store中的CatalogDao使用了DAO模式,从而完成Fast-Lane Reader模式,以便能快速的输出数据库元素
列表,同时使用for page-by-page iteration完成每页的输出显示。
在CatalogDAOImpl 中基本返回的是Page,也就是说,在CatalogDAOImpl的具体JDBC数据库查询时,就将Page功
能融入其中,从而一步到位的完成输出显示。
但在实际系统应用中,我们都有用户权限约束,也就是说,并不是每条数据库记录都能被显示输出,有些用户
就只能看到他被授权看到的的记录。
Jive中的Iterator模式就很好的解决了这个问题,Jive中使用Proxy模式完成用户权限级别的验证,同时为了更
快的获得数据库记录和节约内存,Jive专门建立了自己的Iterator模式,这些一开始让人疑惑,直接使用
Collection的Iterator不是更好,虽然简单方便了,但是前提是在内存中要先开辟一块Collection内存,如果
数据库记录很大,将耗费很多内存,致使系统瘫痪(细节讨论见
http://www.jdon.com:81/jive/thread.jsp?forum=16&thread=302)
Jive的Iterator不只是传递了数据库指针,而且加载了权限验证功能,因此,这一模式是实用可行的,那么在
我们自己的EJB应用中如何综合这两个系统的模式优点?
这其中应该有很多中间方案可行,如果你有兴趣可以贴出你的想法,我目前采取的是DAO模式和Jive的Iterator
模式集合,也就是说,在自己的EJB中不直接返回Page 而是返回Iterator,这个Iterator是类似Jive中的
DatabaseObjectIterator。
简单的说,由于Jive不是EJB架构,所以,将Jive中的访问数据库段用DAO模式替代,其他都可以照搬Jive的
Iterator模式,关于前端JSP页面的分页输出,这时可以参考Pet Store的page-by-page iteration模式,也就
是说,根据Iterator模式再拓展写Page,结构和功能类似Pet store的Page.
这里只提供一个大体思路,如果要写透彻真是很长,看看平常我们以前用ASP
PHP做的数据库查询分页的简单功
能蕴含这么多新的思想,其实这些思想也是在ASP PHP应付大数据库量失败的总结,所以软件
质量控制是显得多
么重要。
我用Iterator的程序代码:
public interface DataListIterator
{
/**
* 功能类似于java.util.Iterator.hasNext()
*
* @return 如果有下一个元素,返回true
* @throws Exception
*/
public boolean hasNext() throws Exception;
/**
* 功能类似于java.util.Iterator.next(),但是返回的是数据库查询的结果
* 的字段值字符串数组。
*
* @return String[] 字段值字符串数组
* @throws Exception
*/
public String[] next() throws Exception;
}
public interface DataList{
/**
* 取出指定位置查询结果中的字段值,放到一个字符串数组中并返回。
* 功能类似于java.util.List.get(int)
*
* @param index 查询结果的索引
* @return String[] 结果中的字段值数组
*
* @throws Exception
*/
public String[] get(int index) throws Exception;
/**
* 检查查询结果的集和是否为空集合
*
* @return boolean true表示空集合
* @throws Exception
*/
public boolean isEmpty() throws Exception;
/**
* 检查是否还有下一个查询结果
*
* @return boolean true表示有下一个
* @throws Exception
*/
public boolean hasNext() throws Exception;
/**
* 检查在指定位置上是否有查询结果
*
* @param index 查询结果的索引
* @return boolean true表示有查询结果
* @throws Exception
*/
public boolean isElementExist(int index) throws Exception;
/**
* 把游标放到指定的位置上,功能类似于java.
sql.ResultSet.absolute(int)
*
* @param index 指定的位置,从0开始
* @return boolean true表示操作成功
* @throws Exception
*/
public boolean absolute(int index) throws Exception;
/**
* 把游标放到查询结果的最前面,功能类似于java.sql.ResultSet.beforeFirst()
*
* @throws Exception
*/
public void beforeFirst() throws Exception;
/**
* 把游标放到查询结果的第一个,功能类似于java.sql.ResultSet.first()
*
* @return boolean true表示移动成功
* @throws Exception
*/
public boolean first() throws Exception;
/**
* 把游标放到查询结果的最后一个,功能类似于java.sql.ResultSet.last()
*
* @return boolean true表示移动成功
* @throws Exception
*/
public boolean last() throws Exception;
/**
* 取得整个查询结果的大小,功能类似于java.util.List.size()
*
* @return size 查询结果的大小
* @throws Exception
*/
public int size() throws Exception;
/**
* 提供一个可以遍历查询结果的对象,功能类似于java.util.List.iterator()
*
* @return DataListIterator 可以遍历查询结果的对象
* @throws Exception
*/
public DataListIterator iterator() throws Exception;
}
public interface DataListHandler{
/**
* 得到查询结果的一个子集
*
* @param startIndex 子集的起始位置
* @param count 子集的个数
* @return Datalist 返回一个子集
* @throws Exception
*/
public DataList getListChunk(int startIndex, int count) throws Exception;
/**
* 取得整个查询结果的大小,功能类似于java.util.List.size()
*
* @return size 查询结果的大小
* @throws Exception
*/
public int size() throws Exception;
/**
* 检查子集的前面是否还有查询结果
*
* @return boolean true表示前面还有查询结果
*/
public boolean hasPrevious();
/**
* 检查子集的后面是否还有查询结果
*
* @return boolean true表示后面还有查询结果
* @throws Exception
*/
public boolean hasNext() throws Exception;
/**
* 关闭对象
* @throws Exception
*/
public void close() throws Exception;
}
* @version 1.0
*
* Page实现了DataListHandler。
*
* 用于操作ResultSetDataList对象,对查询结果集进行分页显示。在进行显示的期间,必须
* 保持数据库连接Connection和结果集ResultSet没有关闭。
* 基本使用方法举例:
*
* int index = 4, count = 10;//显示第5到第14条记录
* Connection conn = Pool.getConnection();
* //(1)使用一个QueryDAO的具体子类来初始化页对象
* ResultSetQueryDAO dao = new ResultSetQueryDAO();
* ResultSetPage page = new ResultSetPage(conn, dao);
*
* //(2)或者直接使用一个ResultSet对象来初始化页对象
* //ResultSet rs = ...;
* //ResultSetPage page = new ResultSetPage(conn, rs);
*
* //需要显示的当前页chunk
* DataList chunk = page.getListChunk(index, count);
* //当前页的前后是否还有记录,用于显示PRVEIOUS和NEXT按钮
* boolean hasPrevious = page.hasPrevious();
* boolean hasNext = page.hasNext();
* //遍历显示当前页的记录
* DataListIterator it = chunk.iterator();
* while (it.hasNext())
* {
* String[] valuesOfRow = it.next();
* for(int i = 0; i < valuesOfRow.length; i++)
* System.out.println(valuesOfRow[i]);
* }
*
*
*
* @see QueryDAO
* @see DataListIterator
*/
import java.sql.*;
public class ResultSetPage implements DataListHandler{
private Connection conn = null;
private ResultSet rs = null;
private ResultSetDataList dl = null;
private RowMapper mapper = null;
private QueryDAO dao = null;
private int indexOfDataList = 0, countPerPage = 0;
/**
* 构造函数,提供用于查询的DAO具体对象。
*
* @param conn 数据库连接
* @param dao 用于查询的DAO具体对象
* @throws
SQLException
*
*/
public ResultSetPage(Connection conn, QueryDAO dao) throws SQLException
{
this.conn = conn;
//使用使用DAO具体对象查询
this.rs = (ResultSet)dao.doQuery(conn);
initiateDataList();
}
/**
* 构造函数,直接提供查询的结果集。
*
* @param conn 数据库连接
* @param rs 查询的结果集
* @throws SQLException
*/
public ResultSetPage(Connection conn, ResultSet rs) throws SQLException
{
this.conn = conn;
this.rs = rs;
initiateDataList();
}
/**
* 得到被查询的字段名称数组
*
* @return String[] 字段名称数组
*/
public String[] getColumns()
{
return mapper.getRowColumns();
}
/**
* 得到查询结果的一个子集
*
* @param startIndex 子集的起始位置, 从0开始
* @param count 子集的个数
* @return Datalist 返回一个子集
* @throws Exception
*/
public DataList getListChunk(int startIndex, int count) throws Exception
{
if(dl == null)
throw new Exception("还没有执行查询或者查询过程出现异常!");
indexOfDataList = Math.abs(startIndex);
countPerPage = Math.abs(count);
return new ResultSetDataListChunk(dl, indexOfDataList, countPerPage);
}
/**
* 取得整个查询结果的大小,功能类似于java.util.List.size()
*
* @return size 查询结果的大小
* @throws SQLException
*/
public int size() throws SQLException
{
if(dl == null)
throw new SQLException("还没有执行查询或者查询过程出现异常!");
return dl.size();
}
/**
* 检查子集的前面是否还有查询结果
*
* @return boolean true表示前面还有查询结果
*/
public boolean hasPrevious()
{
if(indexOfDataList == 0)
return false;
else
return true;
}
/**
* 检查子集的后面是否还有查询结果
*
* @return boolean true表示后面还有查询结果
* @throws SQLException
*/
public boolean hasNext() throws SQLException
{
if(indexOfDataList + countPerPage >= dl.size())
return false;
else
return true;
}
/**
* 关闭对象
* @throws SQLException
*/
public void close() throws SQLException
{
if(rs != null)
{
rs.close();
rs = null;
}
if(conn != null)
{
conn.close();
conn = null;
}
mapper = null;
}
/**
* helper方法,设置印射,生成包含结果计的ResultSetDataList对象
*
* @throws SQLException
*/
private void initiateDataList() throws SQLException
{
//设置印射,生成包含结果计的ResultSetDataList对象
mapper = new ResultSetRowMapper(rs);
dl = new ResultSetDataList(mapper, conn, rs);
}
}
public interface QueryDAO{
/**
* 执行查询操作
*
* @param conn 数据库连接
* @return Object 查询结果(ResultSet或者RowSet)。
* @throws SQLException
*/
public Object doQuery(java.sql.Connection conn) throws java.sql.SQLException;
}
在jsp/serlvet中调用:
ResultSetPage groupPage = groupManager.groups();
DataList chunk = groupPage.getListChunk(0, 50); //chunk.size()
为结果集大小,可用于分页
DataListIterator it = chunk.iterator();
while (it.hasNext())
{
String[] valuesOfRow = it.next();
}
作者简介:
21岁,大学计算机系专业,现正致力于JAVA
开发,对JAVA的桌面应用系统开发和WEB相关技术有一定研究,曾开发j
ant,jzip,小型
游戏,JDBC..etc,对J2EE/
J2ME充满信心。
你可以通过网站:http://www.fls-cts.com/kkjvk/、 OICQ:29578635,878229,email:kkjvk12@yeah
.net和vincent联系。
原文转自:http://www.ltesting.net