【整理】Hibernate分页原理简单分析
【背景说明】
面试时曾被问得不知所以然,故收集整理之。
【使用概览】整理自:Hibernate分页查询经典实例总结
通常使用的Hibernate查询是三种:hql查询,QBC查询和QBE查询。
1、QBE(Qurey By Example)检索方式
QBE是最简单的,但是功能也是最弱的,QBE的功能不是特别强大,仅在某些场合下有用。一个典型的使用场合就是在查询窗口中让用户输入一系列的查询条件,然后返回匹配的对象。QBE只支持=和like比较运算符,无法不大区间值,及其或的匹配。在这种情况下,还是采用HQL检索方式或QBC检索方式。
2、QBC(Qurey By Criteria)检索方式
采用HQL检索方式时,在应用程序中需要定义基于字符串形式的HQL查询语句。QBCAPI提供了检索对象的另一种方式,它主要由Criteria接口、Criterion接口和Restrictions接口组成,它支持在运行时动态生成查询语句。比较常见的是两种传参方式:一种是用map传参,另一种是用Criterion…不定参数传参。
3、HQL(Hibernate Query Language)检索方式 HQL是面向对象的查询语言,它和SQL查询语言有些相识。在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。
------------------------------------------------------------华丽的分割线---------------------------------------------------
【原理分析】整理自:关于java中的Hibernate分页管理简单分析
以下向大家介绍Hibernate分页,Hibernate中,通过对不同数据库的统一接口设计,实现了透明化、通用化的分页实现机制。
通过Criteria.setFirstResult和Criteria.setFetchSize方法设定分页范围,如:
Criteria criteria = session.createCriteria(TUser.class); criteria.add(Expression.eq("age", "20")); //从检索结果中获取第100条记录开始的20条记录 criteria.setFirstResult(100); criteria.setFetchSize(20);
通过Query.setFirstResult和Query.setMaxResults方法也可以设定分页范围,如:
Query query = session.createQuery("from TUser"); query.setFirstResult(100); query.setMaxResults(20); // query.setFetchSize(20); List list = query.list();
Hibernate中,抽象类org.hibernate.dialect.Dialect指定了所有底层数据库的对外统一接口,通过针对不同数据库提供相应的Dialect实现,数据库之间的差异性得以消除,从而为上层机制提供了透明的、数据库无关的存储层基础。对于分页机制而言,Dialect中定义了一个方法如下:
/** * Add a LIMIT clause to the given SQL SELECT * * @return the modified SQL */ public String getLimitString(String querySelect, boolean hasOffset)) { throw new UnsupportedOperationException( "paged queries not supported" ); } public String getLimitString(String querySelect, int offset, int limit) { return getLimitString( querySelect, offset>0 ); }
此方法用于在现有Select语句基础上,根据各个数据库自身特性,构造对应的记录返回限定子句。如MySQL中对应的记录限定子句为Limit,Oracle中,通过rownum子句实现。
MySQLDialect中的getLimitString实现:
public String getLimitString(String sql, boolean hasOffset) { return new StringBuffer( sql.length()+20 ).append(sql).append( hasOffset ? " limit ?, ?" : " limit ?").toString(); }
MySQLDialect.getLimitString方法的实现实际上是在给定的Select语句后追加MySQL所提供的专有SQL子句limit来实现。
Oracle9Dialect中的getLimitString实现:
public String getLimitString(String sql, boolean hasOffset) { sqlsql = sql.trim(); boolean isForUpdate = false; if ( sql.toLowerCase().endsWith(" for update") ) { sqlsql = 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特有的rownum子句来实现数据部分的读取。
SQLServerDialect中的getLimitString实现:
public String getLimitString(String querySelect, int offset, int limit) { if ( offset > 0 ) { throw new UnsupportedOperationException( "sql server has no offset" ); } return new StringBuffer( querySelect.length()+8 ).append(querySelect).insert( getAfterSelectInsertPoint(querySelect), " top " + limit ).toString(); }
通过SQLServer特有的top子句实现。
HSQLDialect中的getLimitString实现:
public String getLimitString(String sql, boolean hasOffset) { return new StringBuffer( sql.length() + 10 ).append( sql ).insert( sql.toLowerCase().indexOf( "select" ) + 6, hasOffset ? " limit ? ?" : " top ?" ).toString(); }
大多数主流数据库都提供了数据部分读取机制,而对于某些没有提供相应机制的数据库而言,Hibernate也通过其他途径实现了分页,如通过Scrollable ResultSet,如果JDBC不支持Scrollable ResultSet,Hibernate也会通过ResultSet的next方法进行记录定位。Hibernate通过底层对分页机制的良好封装,使得开发人员无需关心数据分页的细节实现,将数据逻辑和存储逻辑分离开来,在提高生产效率的同时,也大大加强了系统在不同数据库平台之间的可移植性。
好了,hibernate的分页原理整理到这,欢迎补充:)睡觉~~
PS:还有一个网上转载比较多的是robbin的老帖子Hibernate实现分页查询的原理分析,有兴趣的可以看看:)