iLeichun

当前位置:首页Hibernate

Hibernate hql时间比较

分类:Hibernate  来源:网络  时间:2011-1-27 23:48:53

 

/**
     * 获取现在时间
     *
     * @return 返回时间类型 yyyy-MM-dd HH:mm:ss
     */ 
     public static java.sql.Timestamp getNowSqlDate() {
     Date currentTime = new Date();
     SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     String dateString = formatter.format(currentTime);
     java.sql.Timestamp currentTime_2 = java.sql.Timestamp.valueOf(dateString);
     return currentTime_2;
     }

返回24时制当前时间yyyy-MM-dd HH:mm:ss 如果yyyy-MM-dd hh:mm:ss则返回12时制的当前时间

hql代码

Map filterMap1=new HashMap();         
filterMap1.put("kcjyid", kcjy.getId().toString());
filterMap1.put("nowtime", DateUtil.getNowSqlDate());

Collection<Object> teachksaplist=(Collection<Object>) serviceFactory.getAdminService().list1("from Ksap as b where b.kcjyId=:kcjyid and b.ksend <:nowtime order by b.ksend ", page, 1, filterMap1);

ksend 为Ksap 实体中的属性类型为Date
 

hibernate实现oracle的主键自增长

分类:Hibernate  来源:网络  时间:2010-10-19 23:31:20

在很多情况下,我们使用Hibernate在已经建立好数据库的基础上。在oracle中,如果已经建立好的数据库中使用了sequence ,则可以按照下面的步骤把它引入到Hibernate中:

1、在oracle 首先创建sequence

create sequence seq_id

minvalue 1

start with 1

increment by 1

cache 20;

2.在你的hbm.xml中的配置

《id column=“ID0000” name=“id” type=“integer”》

《generator class=“sequence”》

《param name=“sequence”》seq_id《/param》

《/generator》

《/id》

这样再插入数据的时候,Hibernate回自动生成如下语句:

hibernate : select seq_id.nextval from dual

hibernate : insert into YXJK.T_YXJK_WHRYTXL (XM0000, ZW0000, LXDH00, SJHM00, DZYJ00,

IP0000, ID0000) values (?, ?, ?, ?, ?, ?, ?)

自动生成下一个序列值,然后将对象插入表中。

在使用的时候需要注意,Hibernate对于sequence的主键的要求是一定要是shor,long,或者integer

根据hibernate的文档,有两种方式实现实体对象的主键自动增长。

第一种:设置ID的增长策略是sequence,同时指定sequence的名字,最好每个表建一个sequence,此种做法就如同MS-SQL,MY-SQL中的自动增长一样,不需要创建触发器,具体的oracle数据库脚本及hibernate配置文件如下:

[1]oracle数据表的创建脚本:

Java代码

1. CREATE TABLE DEPARTMENT (

2. ID NUMBER(19,0) DEFAULT ‘0’ NOT NULL,

3. NAME VARCHAR2(255) NOT NULL,

4. DESCRIPTION CLOB

5. );

6. ALTER TABLE DEPARTMENT ADD CONSTRAINT PRIMARY_0 PRIMARY KEY(ID) ENABLE;

7. ALTER TABLE DEPARTMENT ADD CONSTRAINT UK_DEPARTMENT_1 UNIQUE (NAME);

8.

9. CREATE SEQUENCE DEPARTMENT_ID_SEQ MINVALUE 10000 MAXVALUE 999999999999999999999999 INCREMENT BY 1 NOCYCLE;

复制代码

创建DEPARTMENT表,并为DEPARTMENT表创建一个单独的SEQUENCE,名字为SEQUENCE_ID_SEQ,并不需要创建触发器。

[2]hibernate映射文件的配置:

Java代码

# 《?xml version=“1.0”?》

# 《!DOCTYPE hibernate-mapping PUBLIC

# “-//Hibernate/Hibernate Mapping DTD 3.0//EN”

# “http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd”》

# 《hibernate-mapping package=“com.liyanframework.demo.domain”》

# 《class name=“Department” table=“DEPARTMENT”》

# 《id name=“id” column=“ID”》

# 《generator class=“sequence”》

# 《param name=“sequence”》DEPARTMENT_ID_SEQ《/param》

# 《/generator》

# 《/id》

# 《property name=“name” column=“NAME” type=“string” /》

# 《property name=“description” column=“DESCRIPTION” type=“text” /》

# 《/class》

# 《/hibernate-mapping》

复制代码

在hibernate映射文件中,对ID的生成策略选择sequence,指定sequence的名字DEPARTMENT_ID_SEQ就可以了,当你保存新对象的时候,hibernate会自动取得DEPARTMENT_ID_SEQ.NEXTVAL作为新对象的ID保存到数据库,所以不需要再使用触发器再来生成新记录的ID。

Hibernate一对多关系的处理

分类:Hibernate  来源:网络  时间:2010-9-14 23:40:19

  一对多关系式Hibernate中的重要知识。感觉在Hibernate学习中最重要的也是最难的问题就是配置文件了,对于Hibernate一对多关系的配置***.hbm.xml现总结如下:


  ?xml version="1.0" encoding="gbk"?
  !DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
  hibernate-mapping package="此处填写包文件" 
  class name="一对多中一的实体类的类名" table="所对应的数据库的表名" 
  id name="所对应的实体类的属性(id)" column="所对应的数据表的字段(**_id)" unsaved-value="null" 
  generator class="native"/
  /id
  property name="对应数据表中其他的字段" /property
  set name="对应实体类中的那个集合的属性" inverse="true" lazy="true" cascade="save-update" key column="对应其外键的字段 "/one-to-many class="对应一对多中多的实体类的类名"/ /set/classclass name="对应一对多中多的实体类的类名" table="相对应的数据表名" 
  id name="id" column="对应表的主键字段名" unsaved-value="null" 
  generator class="native"//idproperty name="对应数据表中的其他字段" /propertymany-to-one name="对应一对多中一的实体类的属性" column="对应实体类中的那个集合的属性" /many-to-one/class/hibernate-mapping

  Hibernate一对多关系的配置代码如下:


!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" hibernate-mapping package="entity.test"  class name="Person" table="Person1"  id name="pid" column="p_id" unsaved-value="null"  generator class="native"/ /id property name="name" /property set name="computers" inverse="true" lazy="true" cascade="save-update"  key column="p_id"/ one-to-many class="Computer"/ /set /class class name="Computer" table="Computer"  id name="id" column="c_id" unsaved-value="null"  generator class="native"/ /id property name="name" /property many-to-one name="person" column="p_id" /many-to-one /class /hibernate-mapping !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" hibernate-mapping package="entity.test"  class name="Person" table="Person1"  id name="pid" column="p_id" unsaved-value="null"  generator class="native"/ /id property name="name" /property set name="computers" inverse="true" lazy="true" cascade="save-update"  key column="p_id"/ one-to-many class="Computer"/ /set /class class name="Computer" table="Computer"  id name="id" column="c_id" unsaved-value="null"  generator class="native"/ /id property name="name" /property many-to-one name="person" column="p_id" /many-to-one /class /hibernate-mapping 

  整个的Hibernate技术就是围绕着配置文件去发展。

Hibernate批量更新与删除实例浅析

分类:Hibernate  来源:网络  时间:2010-9-14 23:39:48

  在我们的Java项目中,批量更新是指在一个事务中更新大批量数据,批量删除是指在一个事务中删除大批量数据。批量删除虽然在Hibernate里也可以实现,但因Hibernate的实现机制是一个一个删除,在数量大的情况下很影响效率;其实Hibernate提供的JDBC接口,可以方便的进行批量的更新和删除。

  以下程序直接通过Hibernate API批量更新CUSTOMERS表中年龄大于零的所有记录的AGE字段:

 

  如果CUSTOMERS表中有1万条年龄大于零的记录,那么Session的find()方法会一下子加载1万个Customer对象到内存。当执行tx.commit()方法时,会清理缓存,Hibernate执行1万条更新CUSTOMERS表的update语句:

 

  以上批量更新方式有两个缺点:

  (1)占用大量内存,必须把1万个Customer对象先加载到内存,然后一一更新它们。

  (2)执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新一万个Customer对象,频繁的访问数据库,会大大降低应用的性能。

  为了迅速释放1万个Customer对象占用的内存,可以在更新每个Customer对象后,就调用Session的evict()方法立即释放它的内存:


tx = session.beginTransaction();   
Iterator customers=session.find("from Customer c where c.age>0").iterator();   
while(customers.hasNext()){Customer customer=(Customer)customers.next();   
customer.setAge(customer.getAge()+1);   
session.flush();session.evict(customer);   
}   
tx.commit();session.close(); 

  在以上程序中,修改了一个Customer对象的age属性后,就立即调用Session的flush()方法和evict()方法,flush()方法使Hibernate立刻根据这个Customer对象的状态变化同步更新数据库,从而立即执行相关的update语句;evict()方法用于把这个Customer对象从缓存中清除出去,从而及时释放它占用的内存。

  但evict()方法只能稍微提高批量操作的性能,因为不管有没有使用evict()方法,Hibernate都必须执行1万条update语句,才能更新1万个Customer对象,这是影响批量操作性能的重要因素。假如Hibernate能直接执行如下SQL语句:

  


update CUSTOMERS set AGE=AGE+1 where AGE>0;

  那么,以上一条update语句就能更新CUSTOMERS表中的1万条记录。但是Hibernate并没有直接提供执行这种update语句的接口。应用程序必须绕过Hibernate API,直接通过JDBC API来执行该SQL语句:

 

 

  以上程序演示了绕过Hibernate API,直接通过JDBC API访问数据库的过程。应用程序通过Session的connection()方法获得该Session使用的数据库连接,然后通过它创建PreparedStatement对象并执行SQL语句。值得注意的是,应用程序仍然通过Hibernate的Transaction接口来声明事务边界。

  如果底层数据库(如Oracle)支持存储过程,也可以通过存储过程来执行批量更新。存储过程直接在数据库中运行,速度更加快。在Oracle数据库中可以定义一个名为batchUpdateCustomer()的存储过程,代码如下:

 

 

  以上存储过程有一个参数p_age,代表客户的年龄,应用程序可按照以下方式调用存储过程:

 

 

  从上面程序看出,应用程序也必须绕过Hibernate API,直接通过JDBC API来调用存储过程。

  Session的各种重载形式的update()方法都一次只能更新一个对象,而delete()方法的有些重载形式允许以HQL语句作为参数,例如:


 session.delete("from Customer c where c.age>0");

  如果CUSTOMERS表中有1万条年龄大于零的记录,那么以上代码能删除一万条记录。但是Session的delete()方法并没有执行以下delete语句:


 delete from CUSTOMERS where AGE>0;

  Session的delete()方法先通过以下select语句把1万个Customer对象加载到内存中:


  select * from CUSTOMERS where AGE>0;

  接下来执行一万条delete语句,逐个删除Customer对象:

 

  由此可见,直接通过Hibernate API进行批量更新和批量删除都不值得推荐。而直接通过JDBC API执行相关的SQL语句或调用相关的存储过程,是批量更新和批量删除的最佳方式,这两种方式都有以下优点:

  (1)无需把数据库中的大批量数据先加载到内存中,然后逐个更新或修改它们,因此不会消耗大量内存。

  (2)能在一条SQL语句中更新或删除大批量的数据。


delete from CUSTOMERS where ID=i;   
delete from CUSTOMERS where ID=j;   
……   
delete from CUSTOMERS where ID=k; 


tx = session.beginTransaction();   
Connection con=session.connection();   
String procedure = "{call batchUpdateCustomer(?) }";   
CallableStatement cstmt = con.prepareCall(procedure);cstmt.setInt(1,0); //把年龄参数设为0cstmt.executeUpdate();   
tx.commit(); 


create or replace procedure batchUpdateCustomer(p_age in number)    
    asbeginupdate CUSTOMERS    
    set AGE=AGE+1 where AGE>p_age;   
    end; 


tx = session.beginTransaction();   
Connection con=session.connection();   
PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGE=AGE+1 "+"where AGE>0 ");stmt.executeUpdate();   
tx.commit(); 


update CUSTOMERS set AGE=? …. where ID=i;   
update CUSTOMERS set AGE=? …. where ID=j;   
……   
update CUSTOMERS set AGE=? …. where ID=k; 


tx = session.beginTransaction();   
Iterator customers=session.find("from Customer c where c.age>0").iterator();   
while(customers.hasNext()){Customer customer=(Customer)customers.next();   
customer.setAge(customer.getAge()+1);   
}   
tx.commit();   
session.close(); 

Hibernate 3.5.0-CR-2发布

分类:Hibernate  来源:网络  时间:2010-8-22 12:08:09

该版本主要是一些bugfix和文档的修正。在接下来的两周时间内,Hibernate主要工作还是对文档进行完善以及来自社区的bug报告,接下来还将发布一个 CR3 版本后,正式版本也将随即发布。

下载地址:http://sourceforge.net/projects/hibernate/files/hibernate3/

hibernate二级缓存

分类:Hibernate  来源:网络  时间:2010-8-21 12:25:32

二级缓存称为进程级缓存或SessionFactory级缓存,它可以被所有session共享,它的生命周期伴随着SessionFactory的生命周期存在和消亡。

第一步:复制ehcache.xml文件到src目录下,配置二级缓存

<defaultCache

maxElementsInMemory="100"  --设置缓存中的最大对象数

eternal="false"            --设置缓存中的对象是不是永不过期,如果设置为true,

timeToIdleSeconds和timeToLiveSeconds就没有意义了

timeToIdleSeconds="120"    --设置空闲时间,多少时间之内没有访问自动清除

timeToLiveSeconds="120"    --对象存活时间,一般情况下大于等于timeToIdleSeconds

diskPersistent="true"

overflowToDisk="true"      --将超出的对象存到硬盘上,存储位置设置,例如:<diskStore path="E:/cache"/>

/>

也可以配置某个特定对象的,如(但一般情况是使用缺省的就可以了):

<cache name="uuser"

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="300"

timeToLiveSeconds="600"

overflowToDisk="true"

/>

第二步:启用二级缓存

1.启用二级缓存,默认是启用的:

<property name="cache.use_second_level_cache">true</property>

2.设置缓存产品,如:

<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

3.指定哪些类使用二级缓存(两种方法):

a.在hibernate.cfg.xml配置文件中定义

如:<class-cache class="lovo.po.User" usage="read-write"/>

b.在映射文件中定义

如:在<class>节点下:<cache usage="read-only"/>

有些类设置二级缓存,有些类不设置二级缓存;所以如果在映射文件中定义二级缓存的话会导致<cache>标签遍布在文件中,

因此常用第一种,即在hibernate.cfg.xml配置文件中定义,这样便于维护和管理。

第三步:二级缓存实例

1.打开两次session,调用load查询

代码部分:

Session session = this.sf.openSession();

User u1 = (User) session.load(User.class, 1);

System.out.println("用户姓名:"+u1.getName());

//关闭seeion,释放一级缓存

session.close();

//重新开启session

session = this.sf.openSession();

User u = (User) session.load(User.class, 1);

System.out.println("用户姓名:"+u.getName());

session.close();

 

执行部分:

如果设置<property name="cache.use_second_level_cache">false</property>,执行结果如下:

Hibernate:

select

user0_.id as id0_0_,

user0_.name as name0_0_,

user0_.password as password0_0_,

user0_.createTime as createTime0_0_,

user0_.expireTime as expireTime0_0_

from

t_user user0_

where

user0_.id=?

用户姓名:wangwu

Hibernate:

select

user0_.id as id0_0_,

user0_.name as name0_0_,

user0_.password as password0_0_,

user0_.createTime as createTime0_0_,

user0_.expireTime as expireTime0_0_

from

t_user user0_

where

user0_.id=?

用户姓名:wangwu

如果设置<property name="cache.use_second_level_cache">true</property>,执行结果如下:

Hibernate:

select

user0_.id as id0_0_,

user0_.name as name0_0_,

user0_.password as password0_0_,

user0_.createTime as createTime0_0_,

user0_.expireTime as expireTime0_0_

from

t_user user0_

where

user0_.id=?

用户姓名:wangwu

用户姓名:wangwu

可见,二级缓存是起作用了的。用get测试是同样的结果

2.在1的代码中加入this.sf.evict(User.class),清除二级缓存中的对象,执行结果就跟设置了<property name="cache.use_second_level_cache">false</property>的效果相同。

当然也可以指定清除哪些对象:this.sf.evict(User.class,1),结果很简单。

3.一级缓存和二级缓存的交互(CacheMode:CacheMode.NORMAL、CacheMode.PUT、CacheMode.GET)

代码部分:

Session session = this.sf.openSession();

//CacheMode.GET只向二级缓存中读数据不写

session.setCacheMode(CacheMode.GET);           ……1

User u1 = (User) session.load(User.class, 1);  ……2

//代码1和代码2顺序不能颠倒,否则CacheMode.GET对1号用户不起作用

System.out.println("用户姓名:"+u1.getName());

session.close();

//重新开启session

session = this.sf.openSession();

User u = (User) session.load(User.class, 1);

System.out.println("用户姓名:"+u.getName());

session.close();

执行结果:

Hibernate:

select

user0_.id as id0_0_,

user0_.name as name0_0_,

user0_.password as password0_0_,

user0_.createTime as createTime0_0_,

user0_.expireTime as expireTime0_0_

from

t_user user0_

where

user0_.id=?

用户姓名:wangwu

Hibernate:

select

user0_.id as id0_0_,

user0_.name as name0_0_,

user0_.password as password0_0_,

user0_.createTime as createTime0_0_,

user0_.expireTime as expireTime0_0_

from

t_user user0_

where

user0_.id=?

用户姓名:wangwu

 

默认情况下又读又写,即为CacheMode.NORMAL

CacheMode.PUT只写不读

hibernate一级缓存

分类:Hibernate  来源:网络  时间:2010-8-21 12:24:55

在hibernate中,由于一级缓存的生命周期跟Session的生命周期一样,所以也可以理解为一级缓存是session缓存。
         一、通过在做查询的时候,有几个查询方法支持一级缓存,它们分别是:load(),get(),iterate(),其中要注意的是iterate方法只对实体对象查询才支持一级缓存,如果使用iterate来查询对象里面的相关属性,则查询的时候不支持一级缓存。
1、load()方法。
例子:
 

1Student = (Student)session.load(Student.class1);
2
System.out.println(s.getName());
3System.out.println("---------------------"
);
4= (Student)session.load(Student.class1
);
5System.out.println(s.getName());

PS:只发出一条sql语句,虽然这里使用两次load方法,但是第一次load方法对于id为1的记录进行加载,到第3行才发出一条sql语句及符合的数据,这样就把数据放在一级缓存里面,在第4行开始继续使用load就从一级缓存里面抽取。

2、get()方法。
例子:
 

1Student = (Student)session.get(Student.class1);
2
            System.out.println(s.getName());
3            System.out.println("---------------------"
);
4            = (Student)session.load(Student.class1
);
5            System.out.println(s.getName());

PS:跟1中load一样,只发出一条sql语句。

3、iterate()方法查询实体对象
例子:
 

1Student student = (Student)session.createQuery("from Student where s.id=1").iterate().next();
2            System.out.println("student.name=" +
 student.getName());
3
            
4            //会发出查询id的sql,不会发出查询实体对象的sql,因为iterate使用缓存

5            student = (Student)session.createQuery("from Student where s.id=1").iterate().next();
6            System.out.println("student.name=" + student.getName());

PS:总共发出3条sql语句,1、2行发出两条语句,1条是查询实体对象的sql,另一条是查询实体对象的name属性,由于使用一级缓存,之前1、2行查询的实体存放在一级缓存里面,所以5、6行利用一级缓存里面的数据只发出一条查询id的sql。

4、iterate()方法查询实体对象属性(不支持一级缓存)
例子:
 

1String name = (String)session.createQuery("select s.name from Student where s.id=1").iterate().next();
2            System.out.println("student.name=" +
 name);
3
            
4            //
iterate查询普通属性,一级缓存不会缓存,所以发出sql
5            //一级缓存是缓存实体对象的

6            name = (String)session.createQuery("select s.name from Student where s.id=1").iterate().next();
7            System.out.println("student.name=" + name);


PS:由于iterate()方法查询实体对象属性,一级缓存不会产生作用,所以发出两条sql语句。

       二、一级缓存是存在的,所以要更加合理的管理好,提高程序的效率,通常都是通过clear(),evict()方法来清除缓存,当不需要使用一级缓存或者是更加高效率使用一级缓存。
              如果在一次性更新或者加入数量量比较大的情况下,更加要管理好一级缓存。
例子:
 

 1for(int i=0;i<10000;i++)
 2            
{
 3                Student = new
 Student();
 4                s.setName("s"+
i);
 5
                session.save(s);
 6                if(i%20==0
)
 7                
{
 8
                    session.flush();
 9
                    session.clear();
10                }

11            }

PS:在数据量比较大的情况下管理一级缓存的做法,一般都是设定一定数量的记录给更新或者保存等操作之后,避免一次性大量的实体数据入库导致内存溢出,所以才去先是用第8行的flush和第9行的clear方法来实现比较好的缓存管理。在数据量特别大的时候,可以使用jdbc来实现,因为 hibernate不太适合于数据量特别大的场合使用,如果连jdbc都满足不了数据量的效率要求,只好利用相关的数据库机制来实现。

Hibernate分页的设计实现

分类:Hibernate  来源:网络  时间:2010-8-21 12:22:34

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

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

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行以下:

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

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

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

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:

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行以下:

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的一大好处是,既兼顾了查询分页的性能,同时又保证了代码在不同的数据库之间的可移植性。