在Hibernate应用中如何处理批量更新和批量删除

  Hibernate 2.1不支持批量更新和删除,

批量更新是指在一个事务中更新大批量数据,批量删除是指在一个事务中删除大批量数据。以下程序直接通过Hibernate API批量更新CUSTOMERS表中年龄大于零的所有记录的AGE字段:


代码内容
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();


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

代码内容
update CUSTOMERS set AGE=? . where ID=i;
update CUSTOMERS set AGE=? . where ID=j;
update CUSTOMERS set AGE=? . where ID=k;

以上批量更新方式有两个缺点:
(1) 占用大量内存,必须把1万个Customer对象先加载到内存,然后一一更新它们。
(2) 执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新一万个Customer对象,频繁的访问数据库,会大大降低应用的性能。
为了迅速释放1万个Customer对象占用的内存,可以在更新每个Customer对象后,就调用Sessionevict()方法立即释放它的内存:

代码内容
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属性后,就立即调用Sessionflush()方法和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语句:

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



以上程序演示了绕过Hibernate API,直接通过JDBC API访问数据库的过程。应用程序通过Sessionconnection()方法获得该Session使用的数据库连接,然后通过它创建PreparedStatement对象并执行SQL语句。值得注意的是,应用程序仍然通过HibernateTransaction接口来声明事务边界。
如果底层数据库(如Oracle)支持存储过程,也可以通过存储过程来执行批量更新。存储过程直接在数据库中运行,速度更加快。在Oracle数据库中可以定义一个名为batchUpdateCustomer()的存储过程,代码如下:

代码内容
create or replace procedure batchUpdateCustomer(p_age in number) as
begin
update CUSTOMERS set AGE=AGE+1 where AGE>p_age;
end;



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

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


从上面程序看出,应用程序也必须绕过Hibernate API,直接通过JDBC API来调用存储过程。
Session
的各种重载形式的update()方法都一次只能更新一个对象,而delete()方法的有些重载形式允许以HQL语句作为参数,例如:

代码内容
session.delete("from Customer c where c.age>0");



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

代码内容
delete from CUSTOMERS where AGE>0;



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

代码内容
select * from CUSTOMERS where AGE>0;



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

代码内容
delete from CUSTOMERS where ID=i;
delete from CUSTOMERS where ID=j;
delete from CUSTOMERS where ID=k;



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

(1)
无需把数据库中的大批量数据先加载到内存中,然后逐个更新或修改它们,因此不会消耗大量内存。
(2)
能在一条SQL语句中更新或删除大批量的数据。

 

Hibernate 2.1 级到Hibernate3.0 2

 

Hibernate2.1中,Sessiondelete()方法有几种重载形式,其中参数为HQL查询语句的delete()方法在 Hibernate3.0中被废弃,而参数为Ojbect类型的的delete()方法依然被支持。delete(Object o)方法用于删除参数指定的对象,该方法支持级联删除。

Hibernate2.1没有对批量更新和批量删除提供很好的支持,参见<<精通 Hibernate>>一书的第13章的 13.1.1 节(批量更新和批量删除),而Hibernate3.0对批量更新和批量删除提供了支持,能够直接执行批量更新或批量删除语句,无需把被更新或删除的对象先加载到内存中。以下是通过Hibernate3.0执行批量更新的程序代码:

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

String hqlUpdate = "update Customer set name = :newName where name = :oldName";

int updatedEntities = s.createQuery( hqlUpdate )

.setString( "newName", newName )

.setString( "oldName", oldName )

.executeUpdate();

tx.commit();

session.close();

以下是通过Hibernate3.0执行批量删除的程序代码:

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

String hqlDelete = "delete Customer where name = :oldName";

int deletedEntities = s.createQuery( hqlDelete )

.setString( "oldName", oldName )

.executeUpdate();

tx.commit();

session.close();

 

1.1.6 createSQLQuery()

 

Hibernate3.0中,Session接口的createSQLQuery()方法被废弃,被移到org.hibernate.classic.Session接口中。Hibernate3.0采用新的SQLQuery接口来完成相同的功能。

 

1.1.7 Lifecycle Validatable 接口

 

LifecycleValidatable 接口被废弃,并且被移到org.hibernate.classic包中。

 

1.1.8 Interceptor接口

 

Interceptor  接口中加入了两个新的方法。 用户创建的Interceptor实现类在升级的过程中,需要为这两个新方法提供方法体为空的实现。此外, instantiate()方法的参数作了修改,isUnsaved()方法被改名为isTransient()

 

1.1.9 UserTypeCompositeUserType接口

 

UserTypeCompositeUserType接口中都加入了一些新的方法,这两个接口被移到org.hibernate.usertype包中,用户定义的UserTypeCompositeUserType实现类必须实现这些新方法。

Hibernate3.0提供了ParameterizedType接口,用于更好的重用用户自定义的类型。

1.1.10 FetchMode

 

FetchMode.LAZY FetchMode.EAGER被废弃。取而代之的分别为FetchMode.SELECT FetchMode.JOIN

 

1.1.11 P ersistentEnum

 

PersistentEnum被废弃并删除。已经存在的应用应该采用UserType来处理枚举类型。

 

1.1.12 Blob Clob的支持

 

HibernateBlobClob实例进行了包装,使得那些拥有BlobClob类型的属性的类的实例可以被游离、序列化或反序列化,以及传递到merge()方法中。

 

1.1.13 Hibernate中供扩展的API的变化

 

org.hibernate.criterion org.hibernate.mapping org.hibernate.persisterorg.hibernate.collection 包的结构和实现发生了重大的变化。多数基于Hibernate

2.1 的应用不依赖于这些包,因此不会被影响。如果你的应用扩展了这些包中的类,那么必须非常小心的对受影响的程序代码进行升级。

 

1.2 元数据的变化

 

1.2.1 检索策略

 

Hibernate2.1 中,lazy属性的默认值为“false”,而在Hibernate3.0中,lazy属性的默认值为“true”。在升级映射文件时,如果原来的映射文件中的有关元素,如、等没有显式设置lazy属性,那么必须把它们都显式的设置为lazy=true”。如果觉得这种升级方式很麻烦,可以采取另一简单的升级方式:在元素中设置: default-lazy=false”。

 

1.2.2 对象标识符的映射

 

unsaved-value属性是可选的,在多数情况下,Hibernate3.0将把unsaved-value="0" 作为默认值。

 

Hibernate3.0 中,当使用自然主键和游离对象时,不再强迫实现Interceptor.isUnsaved()方法。 如果没有设置这个方法,当 Hibernate3.0无法区分对象的状态时,会查询数据库,来判断这个对象到底是临时对象,还是游离对象。不过,显式的使用 Interceptor.isUnsaved()方法会获得更好的性能,因为这可以减少Hibernate直接访问数据库的次数。

 

1.2.3 集合映射

 

元素在某些情况下被和元素替代。此外,Hibernate3.0 元素来替代原来的.元素,用元素来替代原来的元素。

 

1.2.4 DTD

 

对象-关系映射文件中的DTD文档,由原来的:

http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd

改为:

http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd

 

1.3 查询语句的变化

 

Hibernate3.0 采用新的基于ANTLRHQL/SQL查询翻译器,不过,Hibernate2.1的查询翻译器也依然存在。在Hibernate的配置文件中,hibernate.query.factory_class属性用来选择查询翻译器。例如:

1)选择Hibernate3.0的查询翻译器:

hibernate.query.factory_class= org.hibernate.hql.ast.ASTQueryTranslatorFactory

2)选择Hibernate2.1的查询翻译器

hibernate.query.factory_class= org.hibernate.hql.classic.ClassicQueryTranslatorFactory

 

提示:ANTLR是用纯Java语言编写出来的一个编译工具,它可生成Java语言或者是C++的词法和语法分析器,并可产生语法分析树并对该树进行遍历。ANTLR由于是纯Java的,因此可以安装在任意平台上,但是需要JDK的支持。

Hibernate开发小组尽力保证Hibernate3.0的查询翻译器能够支持Hibernate2.1的所有查询语句。不过,对于许多已经存在的应用,在升级过程中,也不妨仍然使用Hibernate2.1的查询翻译器。

值得注意的是, Hibernate3.0的查询翻译器存在一个Bug:不支持某些theta-style连结查询方言:如Oracle8i OracleDialect方言、Sybase11Dialect。解决这一问题的办法有两种:(1)改为使用支持ANSI-style连结查询的方言,如 Oracle9Dialect,2)如果升级的时候遇到这一问题,那么还是改为使用Hibernate2.1的查询翻译器。

 

1.3.1 indices()elements()函数

 

HQL select子句中废弃了indices()elements()函数,因为这两个函数的语法很让用户费解,可以用显式的连接查询语句来替代  select elements(...) 。而在HQLwhere子句中,仍然可以使用elements()函数。

 
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值