分页在任何系统中都是非常头疼的事情,有的数据库在语法上支持分页,而有的数据库则需要使用可滚动游标来实现,并且在不支持可滚动游标的系统上只能使用单向游标逐步接近要取得的数据。
Hibernate提供了一个支持跨系统的分页机制,这样无论底层是什么样的数据库都能用统一的接口进行分页操作。比如下面的代码就是从第500条开始取出100条记录:
[java] view plaincopyprint?
01.Query q = session.createQuery("from FooBar as f");
02.q.setFirstResult(500);
03.q.setMaxResults(100);
04.List l = q.list();
Query q = session.createQuery("from FooBar as f");
q.setFirstResult(500);
q.setMaxResults(100);
List l = q.list();
那么Hibernate底层如何实现分页的呢?Hibernate根据Query拼装SQL语句的地方是在org.hibernate.loader.Loader类的prepareQueryStatement方法中,对分页支持的代码在这一段中可以发现:
[c-sharp] view plaincopyprint?
01.if (useLimit)
02.{
03.sql = dialect.getLimitString(
04. sql.trim(), //use of trim() here is ugly?
05. useOffset ? getFirstRow(selection) : 0,
06. getMaxOrLimit(selection, dialect)
07. );
08. }
if (useLimit)
{
sql = dialect.getLimitString(
sql.trim(), //use of trim() here is ugly?
useOffset ? getFirstRow(selection) : 0,
getMaxOrLimit(selection, dialect)
);
}
此处调用Dialect的getLimitString方法来得到不同平台的分页语句。
在MySQLDialect中是如下实现getLimitString方法的:
[c-sharp] view plaincopyprint?
01.public String getLimitString(String sql, boolean hasOffset)
02.{
03.return new StringBuffer( sql.length()+20 )
04..append(sql)
05..append( hasOffset ? " limit ?, ?" : " limit ?")
06..toString();
07.}
public String getLimitString(String sql, boolean hasOffset)
{
return new StringBuffer( sql.length()+20 )
.append(sql)
.append( hasOffset ? " limit ?, ?" : " limit ?")
.toString();
}
这是MySQL的专用分页语句,再来看Oracle9Dialect:
[java] view plaincopyprint?
01.public String getLimitString(String sql, boolean hasOffset) {
02.
03. sql = sql.trim();
04. boolean isForUpdate = false;
05. if ( sql.toLowerCase().endsWith(" for update") ) {
06. sql = sql.substring( 0, sql.length()-11 );
07. isForUpdate = true;
08. }
09.
10. StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
11. if (hasOffset) {
12. pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
13. }
14. else {
15. pagingSelect.append("select * from ( ");
16. }
17. pagingSelect.append(sql);
18. if (hasOffset) {
19. pagingSelect.append(" ) row_ where rownum <= ?) where rownum_ > ?");
20. }
21. else {
22. pagingSelect.append(" ) where rownum <= ?");
23. }
24.
25. if ( isForUpdate ) {
26. pagingSelect.append( " for update" );
27. }
28.
29. return pagingSelect.toString();
30. }
public String getLimitString(String sql, boolean hasOffset) {
sql = sql.trim();
boolean isForUpdate = false;
if ( sql.toLowerCase().endsWith(" for update") ) {
sql = sql.substring( 0, sql.length()-11 );
isForUpdate = true;
}
StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
if (hasOffset) {
pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
}
else {
pagingSelect.append("select * from ( ");
}
pagingSelect.append(sql);
if (hasOffset) {
pagingSelect.append(" ) row_ where rownum <= ?) where rownum_ > ?");
}
else {
pagingSelect.append(" ) where rownum <= ?");
}
if ( isForUpdate ) {
pagingSelect.append( " for update" );
}
return pagingSelect.toString();
}
Oracle采用嵌套3层的查询语句结合rownum来实现分页,这在Oracle上是最好的方式,因为如果只是一层或者两层的查询语句的rownum不能支持order by。
此外Interbase,PostgreSQL,HSQL等也在语法级别上支持分页,具体实现可以查看相应的Dialect实现。如果数据库不支持分页的SQL语句,那么如果数据库支持可滚动游标,那么Hibernate就会采使用ResultSet的absolute方法直接移到查询起点;否则使用循环语句,通过rs.next一步步移动到要查询的数据处:
[java] view plaincopyprint?
01.final int firstRow = getFirstRow( selection );
02.if ( firstRow != 0 )
03.{
04.if ( getFactory().getSettings().isScrollableResultSetsEnabled() )
05.{
06.// we can go straight to the first required row
07.rs.absolute( firstRow );
08.}
09.else
10.{
11.// we need to step through the rows one row at a time (slow)
12.for ( int m = 0; m < firstRow; m++ ) rs.next();
13.}
14.}
final int firstRow = getFirstRow( selection );
if ( firstRow != 0 )
{
if ( getFactory().getSettings().isScrollableResultSetsEnabled() )
{
// we can go straight to the first required row
rs.absolute( firstRow );
}
else
{
// we need to step through the rows one row at a time (slow)
for ( int m = 0; m < firstRow; m++ ) rs.next();
}
}
可见使用Hibernate,在进行查询分页的操作上,是具有非常大的灵活性,Hibernate会首先尝试用特定数据库的分页sql,如果没用,再尝试Scrollable,如果不支持Scrollable再采用rset.next()移动的办法。这样既兼顾了查询分页的性能,同时又保证了代码在不同的数据库之间的可移植性。
分享到:
相关推荐
高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页...
java实现分页 jsp分页 分页java实现分页 jsp分页 分页java实现分页 jsp分页 分页java实现分页 jsp分页 分页java实现分页 jsp分页 分页java实现分页 jsp分页 分页
分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码...
jQuery分页插件设置分页内容显示数量的分页代码 jQuery分页插件设置分页内容显示数量的分页代码
jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页...
超强php分页打包 通用分页 万能分页 ajax分页 google分页
数据库分页数据库分页数据库分页数据库分页数据库分页
AspNetPager除提供默认的类似于DataGrid和GridView的PostBack分页方式外,还支持通过Url进行分页,象大多数asp程序中分页一样, Url分页方式允许用户通过在浏览器地址栏中输入相应的地址即可直接进入指定页面,也...
自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签...
js分页示例,前台分页,客户端分页,分页机制,js分页
ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询...
sql 高效分页存储过程 sql 高效分页存储过程 sql 高效分页存储过程 sql 高效分页存储过程 sql 高效分页存储过程
php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax分页php+ajax...
可以实现横向分页和纵向分页,主要用的是安卓原生实现
分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具
jsf 分页 jsf 分页 jsf 分页 jsf 分页
各种分页样式 分页风格 分页css 分页cs.htm
自己写的一个js分页控件.已经封装,可以直接使用. 适合各种用途的分页控制. 如果界面不够美观,只需要修改css即可.
数据更新与分页数据更新与分页数据更新与分页数据更新与分页数据更新与分页数据更新与分页数据更新与分页数据更新与分页