`

Hibernate 批量处理

阅读更多

本文转自:http://www.360doc.com/content/10/1115/17/4622367_69599592.shtml

 

另外参考资料:Hibernate 3.5.5-final 官方手册 chapter14 batch processing

 

转贴文内容如下:

批量处理数据

通常,在一个Session对象的缓存中只存放数量有限的持久化对象,等到Session对象处理事务完毕,还要关闭Session对象,从而及时释放Session的缓存占用的内存。
批量处理数据是指在一个事务中处理大量数据。以下程序在一个事务中批量更新CUSTOMERS表中年龄大于零的所有记录的AGE字段:

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

tx.commit();
session.close();
 



如果CUSTOMERS表中有1万条年龄大于零的记录,那么Hibernate会一下子加载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万个Customer对象先加载到内存,然后一一更新它们。
执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新1万个Customer对象,频繁地访问数据库,会大大降低应用的性能。

一般说来,应该尽可能避免在应用层进行批量操作,而应该在数据库层直接进行批量操作,例如直接在数据库中执行用于批量更新或删除的SQL语句,如果批量操作的逻辑比较复杂,则可以通过直接在数据库中运行的存储过程来完成批量操作。
并不是所有的数据库系统都支持存储过程。例如目前的MySQL就不支持存储过程,因此不能通过存储过程来进行批量更新或批量删除。
当然,在应用层也可以进行批量操作,主要有以下方式:
(1)通过Session来进行批量操作。
(2)通过StatelessSession来进行批量操作。
(3)通过HQL来进行批量操作。
(4)直接通过JDBC API来进行批量操作。

9.4.1 通过Session来进行批量操作

Session 的save()以及update()方法都会把处理的对象存放在自己的缓存中。如果通过一个Session对象来处理大量持久化对象,应该及时从缓存中清空已经处理完毕并且不会再访问的对象。具体的做法是在处理完一个对象或小批量对象后,立刻调用flush()方法清理缓存,然后再调用clear()方法清空缓存。

通过Session来进行批量操作会受到以下约束:
(1)需要在Hibernate的配置文件中设置JDBC单次批量处理的数目,合理的取值通常为10到50之间,例如:
hibernate.jdbc.batch_size=20
在按照本节介绍的方法进行批量操作时,应该保证每次向数据库发送的批量SQL语句数目与这个batch_size属性一致。
(2)如果对象采用"identity"标识符生成器,则Hibernate无法在JDBC层进行批量插入操作。
(3)进行批量操作时,建议关闭Hibernate的第二级缓存。本书的姊妹篇《精通Hibernate:高级篇》对第二级缓存做了详细介绍。Session的缓存为Hibernate的第一级缓存,通常它是事务范围内的缓存,也就是说,每个事务都有单独的第一级缓存。SessionFactory的外置缓存为 Hibernate的第二级缓存,它是应用范围内的缓存,也就是说,所有事务都共享同一个第二级缓存。在任何情况下,Hibernate的第一级缓存总是可用的。而默认情况下,Hibernate的第二级缓存是关闭的,此外也可以在Hibernate的配置文件中通过如下方式显式关闭第二级缓存:
hibernate.cache.use_second_level_cache=false

1.批量插入数据
以下代码一共向数据库中插入十万条CUSTOMERS记录,单次批量插入20条CUSTOMERS记录:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //单次批量操作的数目为20
session.flush(); //清理缓存,执行批量插入20条记录的SQL insert语句
session.clear(); //清空缓存中的Customer对象
}
}

tx.commit();
session.close();
 



在以上程序中,每次执行session.flush()方法,就会向数据库中批量插入20条记录。接下来session.clear()方法把20个刚保存的Customer对象从缓存中清空。
为了保证以上程序顺利运行,需要遵守以下约束。
在Hibernate的配置文件中,应该把hibernate.jdbc.batch_size属性也设为20。
关闭第二级缓存。因为假如使用了第二级缓存,那么所有在第一级缓存(即Session的缓存)中创建的Customer对象还要先复制到第二级缓存中,然后再保存到数据库中,这会导致大量不必要的开销。
Customer对象的标识符生成器不能为"identity"。

2.批量更新数据

进行批量更新时,如果一下子把所有对象到加载到Session的缓存中,然后再在缓存中一一更新它们,显然是不可取的。为了解决这一问题,可以使用可滚动的结果集org.hibernate.ScrollableResults,Query的scroll()方法返回一个ScrollableResults 对象。以下代码演示批量更新Customer对象,该代码一开始利用ScrollableResults对象来加载所有的Customer对象:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

ScrollableResults customers= session.createQuery("from Customer")
.scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.setAge(customer.getAge()+1); //更新Customer对象的age属性
if ( ++count % 20 == 0 ) { //单次批量操作的数目为20

session.flush();//清理缓存,执行批量更新20条记录的SQL update语句
session.clear();//清空缓存中的Customer对象
}
}

tx.commit();
session.close();
 



在以上代码中,Query的scroll()方法返回的 ScrollableResults对象中实际上并不包含任何Customer对象,它仅仅包含了用于在线定位数据库中CUSTOMERS记录的游标。只有当程序遍历访问ScrollableResults对象中的特定元素时,它才会到数据库中加载相应的Customer对象。
为了保证以上程序顺利运行,需要遵守以下约束:
在Hibernate的配置文件中,应该把hibernate.jdbc.batch_size属性也设为20。
关闭第二级缓存。假如已经在配置文件中启用了第二级缓存,也可以通过以下方式在程序中忽略第二级缓存:

ScrollableResults customers= session.createQuery("from Customer")
//忽略第二级缓存
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
 



9.4.2 通过StatelessSession来进行批量操作

Session 具有一个用于保持内存中对象与数据库中相应数据保持同步的缓存,位于Session缓存中的对象为持久化对象。但在进行批量操作时,把大量对象存放在 Session缓存中会消耗大量内存空间。作为一种替代方案,可以采用无状态的StatelessSession来进行批量操作。
以下代码利用 StatelessSession来进行批量更新操作:

StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers")
.scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.setAge(customer.getAge()+1); //在内存中更新Customer对象的age属性;
session.update(customer);//立即执行update语句,更新数据库中相应CUSTOMERS记录
}

tx.commit();
session.close();
 



从形式上看,StatelessSession与Session的用法有点相似。StatelessSession与Session相比,有以下区别:
(1)StatelessSession没有缓存,通过StatelessSession来加载、保存或更新后的对象都处于游离状态。
(2)StatelessSession不会与Hibernate的第二级缓存交互。
(3)当调用StatelessSession的save()、update()或delete()方法时,这些方法会立即执行相应的SQL语句,而不会仅仅计划执行一条SQL语句。
(4)StatelessSession不会对所加载的对象自动进行脏检查。所以在以上程序中,修改了内存中Customer对象的属性后,还需要通过StatelessSession的update()方法来更新数据库中的相应数据。
(5)StatelessSession不会对关联的对象进行任何级联操作。举例来说,通过StatelessSession来保存一个Customer对象时,不会级联保存与之关联的Order对象。
(6)StatelessSession所做的操作可以被Interceptor拦截器捕获到,但会被Hibernate的事件处理系统忽略。

  注:本博主已证实,两者都会被忽略,API中有说到,且我做过实践。
(7)通过同一个StatelessSession对象两次加载OID为1的Customer对象时,会得到两个具有不同内存地址的Customer对象,例如:

StatelessSession session = sessionFactory.openStatelessSession();
Customer c1=(Customer)session.get(Customer.class,new Long(1));
Customer c2=(Customer)session.get(Customer.class,new Long(1));
System.out.println(c1==c2); //打印false
 



9.4.3 通过HQL来进行批量操作

Hibernate3 中的HQL(Hibernate Query Language,Hibernate查询语言)不仅可以检索数据,还可以用于进行批量更新、删除和插入数据。批量操作实际上直接在数据库中完成,所处理的数据不会被保存在Session的缓存中,因此不会占用内存空间。
Query.executeUpdate()方法和JDBC API中的PreparedStatement.executeUpdate()很相似,前者执行用于更新、删除和插入的HQL语句,而后者执行用于更新、删除和插入的SQL语句。

1.批量更新数据
以下程序代码演示通过HQL来批量更新Customer对象:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlUpdate =
"update Customer c set c.name = :newName where c.name = :oldName";
int updatedEntities = session.createQuery( hqlUpdate )
.setString( "newName", "Mike" )
.setString( "oldName", "Tom" )
.executeUpdate();

tx.commit();
session.close();
 


以上程序代码向数据库发送的SQL语句为:
update CUSTOMERS set NAME="Mike" where NAME="Tom"

2.批量删除数据
Session的delete()方法一次只能删除一个对象,不适合进行批量删除操作。以下程序代码演示通过HQL来批量删除Customer对象:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlDelete = "delete Customer c where c.name = :oldName";
int deletedEntities = session.createQuery( hqlDelete )
.setString( "oldName", "Tom" )
.executeUpdate();
tx.commit();
session.close();
 


以上程序代码向数据库提交的SQL语句为:

 
delete from CUSTOMERS where NAME="Tom"
 



3.批量插入数据
插入数据的HQL语法为:
insert into EntityName properties_list select_statement
以上EntityName表示持久化类的名字,properties_list表示持久化类的属性列表,select_statement表示子查询语句。
HQL只支持insert into ... select ... 形式的插入语句,而不支持"insert into ... values ... "形式的插入语句。
下面举例说明如何通过HQL来批量插入数据。假定有DelinquentAccount和Customer类,它们都有id和name属性,与这两个类对应的表分别为DELINQUENT_ACCOUNTS和CUSTOMERS表。DelinquentAccount.hbm.xml和 Customer.hbm.xml文件分别为这两个类的映射文件。以下代码能够把CUSTOMERS表中的数据复制到 DELINQUENT_ACCOUNTS表中:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where c.id>1";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();
 



以上程序代码向数据库提交的SQL语句为:

insert into DELINQUENT_ACCOUNTS(ID,NAME) select ID,NAME from CUSTOMERS where ID>1
 



9.4.4 直接通过JDBC API来进行批量操作

当通过JDBC API来执行SQL insert、update和delete语句时,SQL语句中涉及到的数据不会被加载到内存中,因此不会占用内存空间。
以下程序直接通过JDBC API来执行用于批量更新的SQL语句:

Transaction tx = session.beginTransaction();
//获得该Session使用的数据库连接
java.sql.Connection con=session.connection();
//通过JDBC API执行用于批量更新的SQL语句
PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGE=AGE+1 "
+"where AGE>0 ");
stmt.executeUpdate();

tx.commit();
 



以上程序通过Session的connection()方法获得该Session使用的数据库连接,然后通过它创建PreparedStatement对象并执行SQL语句。值得注意的是,应用程序仍然通过Hibernate的Transaction接口来声明事务边界。
值得注意的是,在Hibernate3中,尽管Session的connection()方法还存在,但是已经被废弃,不提倡使用了,不过 Hibernate3提供了替代方案:org.hibernate.jdbc.Work接口表示直接通过JDBC API来访问数据库的操作,Work接口的execute()方法用于执行直接通过JDBC API来访问数据库的操作:

public interface Work {
//直接通过JDBC API来访问数据库的操作
public void execute(Connection connection) throws SQLException;
}
Session的doWork(Work work)方法用于执行Work对象指定的操作,即调用Work对象的execute()方法。Session会把当前使用的数据库连接传给execute()方法。

以下程序演示了通过Work接口以及Session的doWork()方法来执行批量操作的过程:
Transaction tx=session.beginTransaction();
//定义一个匿名类,实现了Work接口
Work work=new Work(){
public void execute(Connection connection)throws SQLException{
//通过JDBC API执行用于批量更新的SQL语句
PreparedStatement stmt=connection
.prepareStatement("update CUSTOMERS set AGE=AGE+1 "
+"where AGE>0 ");
stmt.executeUpdate();
}
};

//执行work
session.doWork(work);
tx.commit();
 



当通过JDBC API中的PreparedStatement接口来执行SQL语句时,SQL语句中涉及到的数据不会被加载到Session的缓存中,因此不会占用内存空间。

分享到:
评论

相关推荐

    Hibernate批量处理

    ### Hibernate批量处理详解 #### 一、批量处理概述 Hibernate作为一种强大的对象关系映射(ORM)框架,提供了多种批量处理的方式以提高数据处理效率。批量处理对于需要在短时间内处理大量数据的应用尤其重要,如...

    Hibernate批量处理数据

    ### Hibernate批量处理数据 #### 一、概述 Hibernate作为一款流行的Java持久层框架,它能够以面向对象的方式处理数据库操作,极大地简化了开发过程。然而,在处理大量数据时,如果仍然采用逐条处理的方式,将会...

    Hibernate批量处理海量数据的方法

    以下是针对Hibernate批量处理海量数据的一些关键知识点和优化策略: 1. **理解Hibernate的工作原理**:Hibernate通过查询数据库获取数据,并将其转化为Java对象存储在内存中,这种做法在处理小量数据时非常便捷,但...

    Hibernate下数据批量处理解决方案

    然而,实际上,通过适当的技术手段,我们可以有效地解决Hibernate在批量处理时可能出现的性能问题。以下是一些关于如何在Hibernate环境下优化批量数据处理的策略。 首先,了解问题的根源。在上述例子中,当尝试向...

    在Hibernate中处理批量更新和批量删除

    本文将深入探讨在Hibernate中处理批量更新和批量删除的策略,以及如何优化这些操作,以提高数据库操作的效率。 ### 批量更新的常规方法 在Hibernate中,最直观的批量更新方式是通过循环遍历查询结果集,并对每个...

    hibernate批量删除

    ### Hibernate批量删除详解 #### 背景与概念 在Java开发中,处理大量数据时,经常需要执行批量操作,如批量更新或批量删除。这些操作对于提高应用程序性能至关重要,尤其是在涉及成千上万条记录的情况下。...

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

    #### 策略一:利用Hibernate的批量处理机制 1. **使用`flush()`和`evict()`方法**:在修改实体后,主动调用`session.flush()`方法,使Hibernate将缓存中的变更同步到数据库,然后调用`session.evict(entity)`方法,...

    hibernate批量操作实例详解

    【hibernate批量操作实例详解】 批量操作是提高数据库处理效率的重要手段,特别是在处理大量数据时,能够显著提升性能并减少资源消耗。在Hibernate中,批量操作主要包括批量插入、批量更新和批量删除。以下是对这些...

    jsp Hibernate批量更新和批量删除处理代码

    在当今的IT开发领域中,特别是在使用Java语言开发的Web应用程序中,JSP和Hibernate是构建动态网站和Web服务时经常使用的两种技术。...开发者应当根据具体的应用场景和数据库特性,选择合适的批量处理策略。

    Hibernate中大量数据的更新

    Hibernate 提供了两种批量更新机制:一级缓存(First-Level Cache)和批量抓取(Batching)。 一级缓存 Hibernate 的一级缓存是指 Session 对象中缓存的所有对象。在批量更新时,如果不及时清除一级缓存,可能会...

    struts2 hibernate spring 整合批量删除源码

    总的来说,这个批量删除源码示例展示了如何利用Struts2处理HTTP请求,Spring管理依赖,以及Hibernate与数据库交互,是学习和理解三大框架整合开发的一个实践案例。通过分析和理解这段代码,开发者可以提升自己在企业...

    JSF+hibernate实现批量删除

    本篇文章将详细探讨如何利用JSF与Hibernate相结合来实现批量删除功能,以及在CRUD(创建、读取、更新和删除)操作中的应用。 首先,我们需要理解JSF的工作原理。JSF是一个基于组件的MVC(Model-View-Controller)...

    jsp Hibernate批量更新和批量删除处理代码.docx

    本文件主要探讨了如何使用Hibernate进行批量更新和批量删除处理,这些操作在处理大量数据时尤其重要,因为它们可以显著提高应用的性能。下面我们将深入解析这两个主题。 批量更新在Hibernate中通常涉及在一个事务中...

    2022年Hibernate下数据批量处理Java教程.docx

    Hibernate 下数据批量处理 Java 教程 本文主要介绍了使用 Hibernate 实现数据批量处理的方法和注意事项。在 Java 中,对数据批量处理的需求非常重要,但许多人对 Java 是否适合批量处理持有怀疑念头。实际上,如果...

    浅析Hibernate下数据批量处理方法.doc

    标题"浅析Hibernate下数据批量处理方法"和描述中提到,早期的Hibernate在批量插入时可能存在效率问题,但最新版本已经进行了优化,例如通过设置`batch_size`参数来提高批量处理的性能。`batch_size`设置的是JDBC...

    彻底解决hibernate常见难点.zip

    Hibernate处理1-N关系时保存技巧、Hibernate缓存机制、Hibernate批量处理数据、Hibernate三种继承映射策略、hibernate映射体系、Hibernate主键生成策略、持久层DAO设计建议、基于xml文件的bean、使用HibernateAPI在...

    Hibernate笔记

    【Hibernate批量处理】 批量处理能提高数据操作效率,减少数据库交互次数。在Hibernate中,批量处理主要包括插入和更新。 A. 批量插入 批量插入时,可以使用for循环来创建和保存对象,为了防止内存溢出,可以在...

    hibernate批量删除.txt

    在进行数据库操作时,经常会遇到需要批量处理数据的情况,例如批量更新或批量删除等。利用Hibernate框架可以方便地实现这些功能,提高开发效率并减少资源消耗。本文将详细介绍如何通过Hibernate进行批量删除,并探讨...

    Java中Hibernate的批量插入

    针对这种情况,Hibernate提供了一些批量处理的解决方案。 批量插入是处理大量数据插入的一种有效方法。通常,初学者可能会尝试一次性创建大量对象并调用Session的save()方法来保存,但这会导致所有对象被存储在...

    java数据批量处理

    在Java编程领域,数据批量处理是一项常见的任务,尤其在大数据、数据库操作以及系统集成等场景中,批量处理能显著提高效率并减少资源消耗。本文将深入探讨Java如何进行数据批量处理,涉及的主要知识点包括批量读取、...

Global site tag (gtag.js) - Google Analytics