问题描述在通常的三层构架下客户通过Browser请求Web服务器查询数据库而查询结果是上千条甚至是上百万条记录要求查询结果传送到客户端浏览器并分页显示
考虑因素
Web服务器的资源消耗包括内存(用来存储查询结果)数据库相关资源(数据库连接对象ResultSet对象等等)
DB服务器资源的消耗包括游标会话等等
网络开销包括与数据库建立会话传输查询结果等等
JDBC中的几个重要Class:
A ResultSet object maintains a cursor pointing to its current row of data Initially the cursor is positioned before the first row The next method moves the cursor to the next row and because it returns false when there are no more rows in the ResultSet object it can be used in a while loop to iterate through the result set
ResultSet是直接在数据库上建立游标然后通过ResultSet的行位置定位接口来获得指定行位置的记录当用户通过get方法获取具体纪录的内容时ResultSet才从数据库把所需数据读到客户端
Oracle的ResultSet实现似乎会在本地缓存用户读取过的数据导致内存消耗会随读取数据的增加而增加这样如果一次查询并读取海量数据即使读出数据后马上丢弃(比如直接写入文件)内存消耗也会随查询结果的增加而递增
The RowSet interface extends the standard javasqlResultSet interface A RowSet object may make a connection with a data source and maintain that connection throughout its life cycle in which case it is called a connected rowset A rowset may also make a connection with a data source get data from it and then close the connection Such a rowset is called a disconnected rowset A disconnected rowset may make changes to its data while it is disconnected and then send the changes back to the original source of the data but it must reestablish a connection to do so
RowSet是JDBC中提供的接口Oracle对该接口有相应实现其中很有用的是 oraclejdbcrowsetOracleCachedRowSet OracleCachedRowSet实现了ResultSet中的所有方法但与ResultSet不同的是OracleCachedRowSet中的数据在Connection关闭后仍然有效
解决方案一直接使用ResultSet来处理
从ResultSet中将查询结果读入collection缓存在HttpSession或有状态bean中翻页的时候从缓存中取出一页数据显示这种方法有两个主要的缺点一是用户可能看到的是过期数据二是如果数据量非常大时第一次查询遍历结果集会耗费很长时间并且缓存的数据也会占用大量内存效率明显下降
对上述方法的一种改进是当用户第一请求数据查询时就执行SQL语句查询获得的ResultSet对象及其要使用的连接对象都保存到其对应的会话对象中以后的分页查询都通过第一次执行SQL获得的ResultSet对象定位取得指定页的记录(使用rslast();rsgetRow()获得总计录条数使用rsabsolute()定位到本页起始记录)最后在用户不再进行分页查询时或会话关闭时释放数据库连接和ResultSet对象等数据库访问资源每次翻页都只从ResultSet中取出一页数据这种方式在某些数据库(如oracle)的JDBC实现中差不多也是回缓存所有记录而占用大量内存同时速度也非常慢
在用例分页查询的整个会话期间一个用户的分页查询就要占用一个数据库连接对象和结果集的游标这种方式对数据库的访问资源占用比较大并且其利用率不是很高
优点减少了数据库连接对象的多次分配获取减少了对数据库的SQL查询执行
缺点占用数据库访问资源-数据库连接对象并占用了数据库上的资源-游标会消耗大量内存
解决方案二定位行集SQL查询
使用数据库产品提供的对查询的结果集可定位行范围的SQL接口技术在用户的分页面查询请求中每次可取得查询请求的行范围的参数然后使用这些参数生产取得指定行范围的的SQL查询语句然后每次请求获得一个数据库连接对象并执行SQL查询把查询的结果返回给用户最后释放说有的数据库访问资源
这种方式需要每次请求时都要执行数据库的SQL查询语句对数据库的访问资源是使用完就立即释放不白白占用数据库访问资源 对特定(提供了对查询结果集可定位功能的)的数据库产品如Oracle(rowid或rownum )DB(rowid或rownum ()) PostgreSQL(LIMIT 和 OFFSET)mySQL(Limit)等(MS SQL Server 没有提供此技术)
下面是在oracle下的查询语句示例
SELECT * FROM ( SELECT row_* rownum rownum_ FROM ( ) row_ WHERE rownum <= {pageNumber*rowsPerPage}) WHERE rownum_ > {(pageNumber)*rowsPerPage}
优点对数据库的访问资源(数据库连接对象数据库游标等)没有浪费这些资源的充分重复的利用
缺点对每次分页面查询请求要频繁的从Web容器中获得数据库访问资源(数据库连接对象和数据库游标)并建立连接要依赖于具体的数据库产品的支持