设为首页 收藏本站
查看: 518|回复: 0

[经验分享] Hibernate处理Oracle的分页,是用rownum。

[复制链接]
YunVN网友  发表于 2016-8-15 07:34:53 |阅读模式
  
代码如下:

Java代码 http://www.javaeye.com/images/icon_copy.gif





  • public String getLimitString(String sql, boolean hasOffset);    
  • {   

  •   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 <= ?");;   
  •   }   

  •   return pagingSelect.toString();;   
  • }  



[java] view plaincopyprint?





  • public String getLimitString(String sql, boolean hasOffset);   
  • {  

  •   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 <= ?");;  
  •   }  

  •   return pagingSelect.toString();;  
  • }  



  public String getLimitString(String sql, boolean hasOffset);
{
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 <= ?");;
}
return pagingSelect.toString();;
}

  

出错前提

如果这里的sql是不带order by的sql,则查询结果没有任何问题。

但是,如果sql中带有order by,则会引起混乱,即相同记录会出现在不同页中。但是,这种混乱的出现通常是在下面的情况下:

1、纪录数足够多(如果表中有lob字段更好:P)

2、插入记录数大于3页,每页最好10+条记录

3、order by字段至少需要有2个值

4、具备相同order by字段的记录数大于3页

5、插入记录后,最好做删除、修改操作,然后再插入记录。保证记录在磁盘环境中的顺序是无序的。

6、如果满足上述条件,但还没有出现混乱现象,则适当的加大纪录数。

大家测试这种现象,不需要通过hibernate,直接通过sql就可查到。下面有由hibernate生成的sql,以供大家试验:

select * from
  ( select row_.*, rownum rownum_ from
     ( select * from T_TABLE tTable where tTable.field1 order by  tTable.field2 desc ) row_ where rownum <= ?) where rownum_ > ?;

错误原因

之所以出现这样的问题,是和oracle处理ROWNUM的原理相关的。

以下是Oracle参考手册上的一段话:

引用


ROWNUM返回第一次从表中选择时返回的行的序列号。第一行的ROWNUM为1,第二行的为2,依此类推。但要注意,即使select语句中一条简单的order by都可能会搞乱ROWNUM(因为ROWNUM是排序前分配给各行的)。
  

解决办法(来自使用手册):

9.3.3. Scrollable iteration
If your JDBC driver supports scrollable ResultSets, the Query interface may be used to obtain a ScrollableResults which allows more flexible navigation of the query results. (Oracle 8.1.6+)

Java代码 http://www.javaeye.com/images/icon_copy.gif





  • Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +   

  •                             "order by cat.name");;   
  • ScrollableResults cats = q.scroll();;   

  • if ( cats.first(); ); {   
  •   

  •     // find the first name on each page of an alphabetical list of cats by name   

  •     firstNamesOfPages = new ArrayList();;   

  •     do {   

  •         String name = cats.getString(0);;   
  •         firstNamesOfPages.add(name);;   
  •     }   

  •     while ( cats.scroll(PAGE_SIZE); );;   
  •   

  •     // Now get the first page of cats   

  •     pageOfCats = new ArrayList();;   
  •     cats.beforeFirst();;   

  •     int i=0;   

  •     while( ( PAGE_SIZE > i++ ); && cats.next(); ); pageOfCats.add( cats.get(1); );;   
  •   
  • }  



[java] view plaincopyprint?





  • Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +  

  •                             "order by cat.name");;  
  • ScrollableResults cats = q.scroll();;  

  • if ( cats.first(); ); {  
  •   

  •     // find the first name on each page of an alphabetical list of cats by name   

  •     firstNamesOfPages = new ArrayList();;  

  •     do {  

  •         String name = cats.getString(0);;  
  •         firstNamesOfPages.add(name);;  
  •     }  

  •     while ( cats.scroll(PAGE_SIZE); );;  
  •   

  •     // Now get the first page of cats   

  •     pageOfCats = new ArrayList();;  
  •     cats.beforeFirst();;  

  •     int i=0;  

  •     while( ( PAGE_SIZE > i++ ); && cats.next(); ); pageOfCats.add( cats.get(1); );;  
  •   
  • }  



Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
"order by cat.name");;
ScrollableResults cats = q.scroll();;
if ( cats.first(); ); {
// find the first name on each page of an alphabetical list of cats by name
firstNamesOfPages = new ArrayList();;
do {
String name = cats.getString(0);;
firstNamesOfPages.add(name);;
}
while ( cats.scroll(PAGE_SIZE); );;
// Now get the first page of cats
pageOfCats = new ArrayList();;
cats.beforeFirst();;
int i=0;
while( ( PAGE_SIZE > i++ ); && cats.next(); ); pageOfCats.add( cats.get(1); );;
}

  

我觉得,如果处理数据库类是一个统一的基类,这种方法不适合用。

11.13 Tips & Tricks

1、Collection elements may be ordered or grouped using a query filter:

Java代码 http://www.javaeye.com/images/icon_copy.gif





  • Collection orderedCollection = s.filter( collection, "order by this.amount" );;   

  • Collection counts = s.filter( collection, "select this.type, count(this); group by this.type" );;  



[java] view plaincopyprint?





  • Collection orderedCollection = s.filter( collection, "order by this.amount" );;  

  • Collection counts = s.filter( collection, "select this.type, count(this); group by this.type" );;  



Collection orderedCollection = s.filter( collection, "order by this.amount" );;
Collection counts = s.filter( collection, "select this.type, count(this); group by this.type" );;

  

2、Collections are pageable by using the Query interface with a filter:

Java代码 http://www.javaeye.com/images/icon_copy.gif





  • Query q = s.createFilter( collection, "" );; // the trivial filter   
  • q.setMaxResults(PAGE_SIZE);;   
  • q.setFirstResult(PAGE_SIZE * pageNumber);;   
  • List page = q.list();;  



[java] view plaincopyprint?





  • Query q = s.createFilter( collection, "" );; // the trivial filter   
  • q.setMaxResults(PAGE_SIZE);;  
  • q.setFirstResult(PAGE_SIZE * pageNumber);;  
  • List page = q.list();;  



Query q = s.createFilter( collection, "" );; // the trivial filter
q.setMaxResults(PAGE_SIZE);;
q.setFirstResult(PAGE_SIZE * pageNumber);;
List page = q.list();;

  

这种方法的效率有待考察。

所有sql不用ROWNUM的解决办法

如果执行所有SQL都不用ROWNUM,那么最简单的办法如下:

1、派生Dialect类

Java代码 http://www.javaeye.com/images/icon_copy.gif





  • package com.xxx.data.db.hibernate;   
  •   

  • import  net.sf.hibernate.dialect.Oracle9Dialect;   
  •   

  • public class Oracle9ThunisoftDialect extends Oracle9Dialect   
  • {   

  •   public Oracle9ThunisoftDialect();   
  •   {   

  •     super();;   
  •   }   
  •   

  •   public boolean supportsLimit();   
  •   {   

  •     return false;   
  •   }   
  • }  



[java] view plaincopyprint?





  • package com.xxx.data.db.hibernate;  
  •   

  • import  net.sf.hibernate.dialect.Oracle9Dialect;  
  •   

  • public class Oracle9ThunisoftDialect extends Oracle9Dialect  
  • {  

  •   public Oracle9ThunisoftDialect();  
  •   {  

  •     super();;  
  •   }  
  •   

  •   public boolean supportsLimit();  
  •   {  

  •     return false;  
  •   }  
  • }  



package com.xxx.data.db.hibernate;
import  net.sf.hibernate.dialect.Oracle9Dialect;
public class Oracle9ThunisoftDialect extends Oracle9Dialect
{
public Oracle9ThunisoftDialect();
{
super();;
}
public boolean supportsLimit();
{
return false;
}
}

  

2、修改hibernate.cfg.xml配置文件

Java代码 http://www.javaeye.com/images/icon_copy.gif





  •  <property name="hibernate.dialect">com.xxx.data.db.hibernate.Oracle9ThunisoftDialect</property>   
  •   

  • <!-- oracle 8.1.6+ -->   

  • <property name="hibernate.jdbc.use_scrollable_resultset">true</property>  



[java] view plaincopyprint?





  •  <property name="hibernate.dialect">com.xxx.data.db.hibernate.Oracle9ThunisoftDialect</property>  
  •   

  • <!-- oracle 8.1.6+ -->  

  • <property name="hibernate.jdbc.use_scrollable_resultset">true</property>  



<property name="hibernate.dialect">com.xxx.data.db.hibernate.Oracle9ThunisoftDialect</property>
<!-- oracle 8.1.6+ -->
<property name="hibernate.jdbc.use_scrollable_resultset">true</property>

  
后记

通常情况下,这种现象很少出现,因为我们程序中缺省的order by字段的“选择性”都很大,比如缺省以日期时间排序,order by字段很少出现重复。上面的现象基本上不会出现。不过,我们公司在做压力测试的时候,同一日期时间的记录插入了N多条,从而满足了上面提到的5个前提条件,因此出现了同一记录在不同页中出现的现象。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-258003-1-1.html 上篇帖子: Oracle主键约束、唯一键约束、唯一索引的区别 下篇帖子: Oracle数据库经典优化之索引原理篇
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表