`

Hibernate实现分页查询的原理分析

阅读更多
Hibernate 可以实现分页查询,例如:
从第2万条开始取出100条记录

Java代码

   1. Query q = session.createQuery("from Cat as c");; 
   2. q.setFirstResult(20000);; 
   3. q.setMaxResults(100);; 
   4. List l = q.list();; 

Query q = session.createQuery("from Cat as c");;
q.setFirstResult(20000);;
q.setMaxResults(100);;
List l = q.list();;



那么Hibernate底层如何实现分页的呢?实际上Hibernate的查询定义在net.sf.hibernate.loader.Loader这个类里面,仔细阅读该类代码,就可以把问题彻底搞清楚。

Hibernate2.0.3的Loader源代码第480行以下:

Java代码

   1. if (useLimit); sql = dialect.getLimitString(sql);;       
   2. PreparedStatement st = session.getBatcher();.prepareQueryStatement(sql, scrollable);; 

if (useLimit); sql = dialect.getLimitString(sql);;
PreparedStatement st = session.getBatcher();.prepareQueryStatement(sql, scrollable);;




如果相应的数据库定义了限定查询记录的sql语句,那么直接使用特定数据库的sql语句。

然后来看net.sf.hibernate.dialect.MySQLDialect:

Java代码

   1. public boolean supportsLimit(); { 
   2.   return true; 
   3. } 
   4. public String getLimitString(String sql); { 
   5.   StringBuffer pagingSelect = new StringBuffer(100);; 
   6.   pagingSelect.append(sql);; 
   7.   pagingSelect.append(" limit ?, ?");; 
   8.   return pagingSelect.toString();; 
   9. } 

public boolean supportsLimit(); {
  return true;
}
public String getLimitString(String sql); {
  StringBuffer pagingSelect = new StringBuffer(100);;
  pagingSelect.append(sql);;
  pagingSelect.append(" limit ?, ?");;
  return pagingSelect.toString();;
}



这是MySQL的专用分页语句,再来看net.sf.hibernate.dialect.Oracle9Dialect:

Java代码

   1. public boolean supportsLimit(); { 
   2.   return true; 
   3. } 
   4.  
   5. public String getLimitString(String sql); { 
   6.   StringBuffer pagingSelect = new StringBuffer(100);; 
   7.   pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");; 
   8.   pagingSelect.append(sql);; 
   9.   pagingSelect.append(" ); row_ where rownum <= ?); where rownum_ > ?");; 
  10.   return pagingSelect.toString();; 
  11. } 

public boolean supportsLimit(); {
  return true;
}

public String getLimitString(String sql); {
  StringBuffer pagingSelect = new StringBuffer(100);;
  pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");;
  pagingSelect.append(sql);;
  pagingSelect.append(" ); row_ where rownum <= ?); where rownum_ > ?");;
  return pagingSelect.toString();;
}



Oracle采用嵌套3层的查询语句结合rownum来实现分页,这在Oracle上是最快的方式,如果只是一层或者两层的查询语句的rownum不能支持order by。

除此之外,Interbase,PostgreSQL,HSQL也支持分页的sql语句,在相应的Dialect里面,大家自行参考。

如果数据库不支持分页的SQL语句,那么根据在配置文件里面
#hibernate.jdbc.use_scrollable_resultset true
默认是true,如果你不指定为false,那么Hibernate会使用JDBC2.0的scrollable result来实现分页,看Loader第430行以下:


Java代码

   1. if ( session.getFactory();.useScrollableResultSets(); ); { 
   2.   // we can go straight to the first required row 
   3.   rs.absolute(firstRow);; 
   4. } 
   5. else { 
   6.   // we need to step through the rows one row at a time (slow); 
   7.   for ( int m=0; m<firstRow; m++ ); rs.next();; 
   8. } 

if ( session.getFactory();.useScrollableResultSets(); ); {
  // 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();;
}


如果支持scrollable result,使用ResultSet的absolute方法直接移到查询起点,如果不支持的话,使用循环语句,rs.next一点点的移过去。

可见使用Hibernate,在进行查询分页的操作上,是具有非常大的灵活性,Hibernate会首先尝试用特定数据库的分页sql,如果没用,再尝试Scrollable,如果不行,最后采用rset.next()移动的办法。

在查询分页代码中使用Hibernate的一大好处是,既兼顾了查询分页的性能,同时又保证了代码在不同的数据库之间的可移植性

再来看net.sf.hibernate.dialect.Oracle9Dialect:

Java代码

   1. public boolean supportsLimit() { 
   2.   return true; 
   3. } 
   4.  
   5. public String getLimitString(String sql) { 
   6.   StringBuffer pagingSelect = new StringBuffer(100); 
   7.   pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( "); 
   8.   pagingSelect.append(sql); 
   9.   pagingSelect.append(" ) row_ where rownum <= ?) where rownum_ > ?"); 
  10.   return pagingSelect.toString(); 
  11. } 

public boolean supportsLimit() {
  return true;
}

public String getLimitString(String sql) {
  StringBuffer pagingSelect = new StringBuffer(100);
  pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
  pagingSelect.append(sql);
  pagingSelect.append(" ) row_ where rownum <= ?) where rownum_ > ?");
  return pagingSelect.toString();
}



Oracle采用嵌套3层的查询语句结合rownum来实现分页,这在Oracle上是最快的方式,如果只是一层或者两层的查询语句的rownum不能支持order by。


Oracle的这种实现如果有order by子句依然有问题。某些时候会导致翻页有记录重复或者遗失,很难找到规律,非常奇怪。

后来去google了一下,有Oracle专家说需要order by的时候必须带上unique的字段,例如主键或者rowid等。

另外,在使用这种采用rownum的查询时,尽管速度相对比较快,但是后台Oracle在内存和CPU的消耗上会增加许多。其实除非结果集非常庞大(几万以上),并且必须翻倒很后面(skip的记录很多),采用ResultSet.absolute方法性能还可以,并没有数量级上的差别。
分享到:
评论

相关推荐

    Hibernate分页查询原理解读

    #### 三、Hibernate分页查询实现原理 ##### 3.1 使用SQL LIMIT实现分页 对于支持LIMIT关键字的数据库(例如MySQL),Hibernate会通过特定的方言(Dialect)来生成包含LIMIT关键字的SQL语句。具体实现如下: ```...

    用Hibernate实现分页查询.docx

    ### 使用Hibernate实现分页查询 #### 一、分页查询概念及原理 分页查询是一种在数据量较大的情况下,为了提高用户体验和系统性能而采取的一种技术手段。它将查询结果分成若干页显示,用户可以通过翻页操作查看不同...

    STRUTS2+HIBERNATE详细的分页实现代码详细的分页实现代码

    #### 分页原理概述 分页是一种常见的技术手段,用于在数据量较大时提高用户体验。其实现的基本思路是将数据分割成多个小部分,每次只显示其中的一部分数据。用户可以通过翻页操作来查看其他部分的数据。 #### ...

    hibernate商品分页展示

    本篇将深入探讨如何利用Hibernate框架来实现商品的分页显示。 首先,我们需要理解分页的基本概念。分页是将大量数据分割成若干个较小的部分,每个部分称为一页,用户可以逐页浏览,而不需要一次性加载所有数据。在...

    基于hibernate实现的分页技术

    ### 基于Hibernate实现的分页技术 在软件开发领域,特别是在J2EE应用程序中,数据展示往往需要处理大量的记录。为了提升用户体验并减轻服务器...希望本文能为正在探索Hibernate分页技术的开发者们提供一定的参考价值。

    基于hibernate实现的分页技术实例分析

    首先,理解Hibernate分页的原理是至关重要的。基本思想是通过设置查询的开始位置(起始索引)和最大返回结果数来实现分页。在Hibernate中,我们可以使用`Query`对象的`setMaxResults()`和`setFirstResult()`方法来...

    spring+hibernate+struts实现分页代码

    "分页.txt"可能是关于分页原理、步骤或最佳实践的文档。 通过以上分析,我们可以看出SSH框架在分页功能中的角色和交互,以及如何在实际开发中利用这些工具和技术来实现高效的数据分页展示。理解并掌握这些知识点...

    jsp hibernate的分页代码第1 3页.docx

    ### Hibernate分页查询的优势 文档中提到:“在查询分页代码中使用Hibernate的一大好处是,既兼顾了查询分页的性能,同时又保证了代码在不同的数据库之间的可移植性。”这意味着通过Hibernate实现分页查询,可以...

    高效率spring+struts+hibernate分页算法

    ### 高效率Spring+Struts+Hibernate分页算法解析 #### 一、引言 在Web应用程序开发过程中,为了提高用户体验以及系统性能,分页显示数据成为了一项必不可少的功能。本篇文章将详细介绍一种基于Spring、Struts和...

    struts+hibernate+sql server2005分页的小项目

    通过理解这些技术的集成和分页原理,开发者可以更好地应对大数据量的展示需求,提升Web应用的性能和用户体验。项目的源码分析有助于学习和掌握这些技术的实际应用,对于Java Web开发的学习和实践具有重要意义。

    struts+spring+hibernate通用分页方法

    通过上述分析可以看出,该通用分页方法充分利用了Struts、Spring 和 Hibernate 的优势,通过清晰的层次划分实现了高效的物理分页功能。这种分页策略不仅适用于当前的示例场景,也具有一定的通用性,可以应用于其他...

    Struts hibernate下的分页代码

    #### 三、分页原理 分页的基本思想是根据用户请求的当前页码,查询出对应的数据集合并显示。为了实现这一目标,我们需要计算出每一页的起始记录索引和结束记录索引,然后通过SQL查询语句获取数据。 #### 四、Page...

    Hibernate框架数据分页技术实例分析

    本篇将深入探讨Hibernate框架实现数据分页的原理、步骤及实践技巧。 1. **数据分页基本思想** - **确定记录跨度**:每页显示的记录数量,可以根据实际需求调整。 - **获取记录总数**:计算所有待显示的记录数量,...

    Hibernate分页

    ### Hibernate分页技术详解 #### 一、概述 在现代软件开发中,特别是Web应用程序,分页是一项非常重要的功能。它不仅可以提高用户体验,还可以优化数据处理效率,避免一次性加载大量数据导致性能问题。Hibernate...

    Oracle分页查询

    #### 六、Hibernate分页查询实现 除了直接使用SQL进行分页查询外,开发人员还经常使用ORM框架如Hibernate来实现分页功能。例如: ```java Query q = session.createQuery("from Catasc"); q.setFirstResult(20000);...

    hibernate动态分表

    本篇文章将深入探讨Hibernate如何实现动态分表,并结合源码分析其工作原理。 首先,了解动态分表的基本概念。动态分表是指在运行时根据某些条件(如时间、用户ID等)自动将数据分配到不同的表中,这样可以避免单一...

    分页显示查询的示例代码

    本示例代码将详细介绍如何实现分页查询的功能,通过阅读和理解代码,你将能够掌握分页的基本原理和实现方式。 首先,我们要明白分页的基本概念。分页是将大量数据分为多个部分(页)进行展示,每次只加载一部分,...

    分页查询研究

    在"源码"这个标签下,我们可以了解到,分页查询的实现往往涉及到数据库操作层的代码,比如Java中的JDBC,ORM框架如MyBatis或Hibernate,它们提供了对分页查询的封装。开发者需要了解这些框架如何设置分页参数,以及...

    shh整合框架实现分页

    #### 分页原理概述 分页的核心思想是通过限制SQL查询语句返回的结果集大小来实现。一般而言,我们需要计算出总页数和当前页的数据范围。具体步骤包括: 1. **计算总记录数**:首先,需要获取数据库中的所有记录数...

    oracle分页查询

    Hibernate框架也支持分页查询,其实现原理大致如下: 1. 使用`setFirstResult()`方法设置分页起始位置。 2. 使用`setMaxResults()`方法设置每页返回的数据量。 ```java Query q = session.createQuery("from Catasc...

Global site tag (gtag.js) - Google Analytics