例如
从第万条开始取出条记录
代码:
Query q = sessioncreateQuery(from Cat as c);
qsetFirstResult();
qsetMaxResults();
List l = qlist();
那么Hibernate底层如何实现分页的呢?实际上Hibernate的查询定义在netsfhibernateloaderLoader这个类里面仔细阅读该类代码就可以把问题彻底搞清楚
Hibernate的Loader源代码第行以下
代码:
if (useLimit) sql = dialectgetLimitString(sql);
PreparedStatement st = sessiongetBatcher()prepareQueryStatement(sql
scrollable);
如果相应的数据库定义了限定查询记录的sql语句那么直接使用特定数据库的sql语句
然后来看netsfhibernatedialectMySQLDialect:
代码:
public boolean supportsLimit() {
return true;
}
public String getLimitString(String sql) {
StringBuffer pagingSelect = new StringBuffer();
pagingSelectappend(sql);
pagingSelectappend( limit ? ?);
return pagingSelecttoString();
}
这是MySQL的专用分页语句再来看netsfhibernatedialectOracleDialect:
代码:
public boolean supportsLimit() {
return true;
}
public String getLimitString(String sql) {
StringBuffer pagingSelect = new StringBuffer();
pagingSelectappend(select * from ( select row_* rownum rownum_
from ( );
pagingSelectappend(sql);
pagingSelectappend( ) row_ where rownum <= ?) where rownum_ > ?);
return pagingSelecttoString();
}
Oracle采用嵌套层的查询语句结合rownum来实现分页这在Oracle上是最快的方式如果只是一层或者两层的查询语句的rownum不能支持order by
除此之外InterbasePostgreSQLHSQL也支持分页的sql语句在相应的Dialect里面大家自行参考
如果数据库不支持分页的SQL语句那么根据在配置文件里面#hibernatejdbcuse_scrollable_resultset true默认是true如果你不指定为false那么Hibernate会使用JDBC的scrollableresult来实现分页看Loader第行以下
代码:
if ( sessiongetFactory()useScrollableResultSets() ) {
// we can go straight to the first required row
rsabsolute(firstRow);
}
else {
// we need to step through the rows one row at a time (slow)
for ( int m=; m<firstRow; m++ ) rsnext();
}
如果支持scrollable result使用ResultSet的absolute方法直接移到查询起点如果不支持的话使用循环语句rsnext一点点的移过去
可见使用Hibernate在进行查询分页的操作上是具有非常大的灵活性Hibernate会首先尝试用特定数据库的分页sql如果没用再尝试Scrollable如果不行最后采用rsetnext()移动的办法
在查询分页代码中使用Hibernate的一大好处是既兼顾了查询分页的性能同时又保证了代码在不同的数据库之间的可移植性