`
yourenyouyu2008
  • 浏览: 286065 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

关于hibernate的批处理

阅读更多

很多系统中大数据批量处理在所难免,网上也有很多关于hibernate的批处理建议,基本上都是一样的,但如果我们的系统设计的比较纯面向对象,这种方案是非常失败的。下面我来讲述下我使用这种方法碰到的问题,然后分析问题的原因,给出我们目前能接受的方案。

传统方案

利用hibernate.jdbc.batch_size参数,session.flush();session.clear();来释放内存


<hibernate-configuration>
<session-factory>
.........
<property name=” hibernate.jdbc.batch_size”>50</property>
.........
<session-factory>
<hibernate-configuration>

 

Session session=HibernateUtil.currentSession();
Transatcion tx=session.beginTransaction();
for(int i=0;i<100000;i++)
...{
Student s=new Student();
s.setName(“Paul”);
session.save(s);
if(i%50==0) //
以每50个数据作为一个处理单元

...{
session.flush(); //
保持与数据库数据的同步

session.clear(); //
清除内部缓存的全部数据,及时释放出占用的内存

}
}
tx.commit();

问题

1)  如果Student的某个属性是别的对象,这样.flush()时会去find每一个关联的对象,这样速度是大打折扣了

2)  session.clear()是个相当耗时的方法,特别是当缓存中数据量大的时候。Hibernate从来不建议程序调用该方法,由于session是基于线程的,所以当一个请求结束后session占用的内存自然会释放,这种速度可以忽略不计。

上面的问题主要出在问题1

新的方案

以空间换时间

针对传统方案一一解决

1)  预先批量查出Student关联的属性对象,这样.flush()时就不需要去find了。

一般我们的对象关联都设置为延迟加载,所以循环时才去find,这样性能也是很慢的

有两种办法来解决

1-1】        select h,m 查询多个对象,用List <Object[]>来接收,这样一次就把需要的数据全查到内存中了

1-2】        join fetch,具体参加jpa语言中的join

2)  不调用.clear(),用做够的内存来跑

 

List <Object[]> addlist=em.createQuery("select h,m from OweTmpVO h,Meter m join fetch m.contract where h.meterid=m.id and h.je<0 and h.cbdate<:date")

       .setParameter("date",date)

       .getResultList();

       System.out.println("--addlist--size:"+addlist.size());

       for(Object[] obs:addlist){

           Meter meter=(Meter)obs[1];

           OweTmpVO oweTmp=(OweTmpVO)obs[0];

          

           Owe newowe=new Owe();

           newowe.setId(oweTmp.getOweid());

           newowe.setQfdate(oweTmp.getCbdate());

           newowe.setQfje(oweTmp.getJe());

           newowe.setMeter(meter);

           newowe.setXzqh(meter.getXzqh());

           OweCustomerInfo oci=new OweCustomerInfo();

           oci.setAddress(meter.getAddress());

           oci.setId(newowe.getId());

           oci.setLxr(meter.getLinkMan().getLxr());

           oci.setName(meter.getUsername());

           oci.setTel(meter.getLinkMan().getTel());

           if (meter.getContract()!=null){

              oci.setHbh(meter.getContract().getHbh());

              oci.setLxr(meter.getContract().getLinkMan().getLxr());

              oci.setName(meter.getContract().getUsername());

              oci.setTel(meter.getContract().getLinkMan().getTel());

           }

           newowe.setCustomerInfo(oci);

           em.persist(newowe);

           em.remove(oweTmp);

       }

需要解决的问题

新的方案需要大量的内存,而虚拟机只能提供1G多的内存,1百万的记录足够让内存溢出。

我们需要将数据分批来处理,比如根据某些条件来限制查出的记录数,或者用分页(一次50万条),还有就是不能让这些分批在同一线程中,在同一线程中就没有分批的意义了(问题同样存在)。我们可以有两种选择

1)  通过页面来手动操作,这样就把每批放到不同的线程中了。但这需要用户参与,一般来说客户不会采取的。

2)  采用异步调用(定时任务),每个任务相当与一个独立的线程,这样就不用考虑session内存释放的问题了。

把每批用一个任务来完成,间隔执行每个任务即可,注意设置任务间隔,尽量保证同一时间只有一个任务在执行,要不内存还是会溢出的。

3)利用ejb来分布式处理,由于ejb可以独立部署,这样就相当于扩大了内存,可以在web层调用多个ejb来不一个大的任务分成多个小的任务来处理。

下面是我们基于seam的异步调用

public void createReportByYear(){

//临时设置session为足够长,与本方案无关

       javax.servlet.http.HttpServletRequest request=(HttpServletRequest)javax.faces.context.FacesContext.getCurrentInstance().getExternalContext().getRequest();

       int timeout=request.getSession().getMaxInactiveInterval();

       request.getSession().setMaxInactiveInterval(60000);

 

       em.createQuery("delete OweYearReport").executeUpdate();

       XzqhVO DCQ=getXzqh();

//处理一个区的数据

       createReportByYear1(DCQ);

//为每个区的任务设置个开始时间

       TimerSchedule timerSchedule0=new TimerSchedule(10l);//马上

       TimerSchedule timerSchedule1=new TimerSchedule(1000*60*1l);//一分钟后

       TimerSchedule timerSchedule2=new TimerSchedule(1000*60*2l);

       TimerSchedule timerSchedule3=new TimerSchedule(1000*60*3l);

       TimerSchedule timerSchedule4=new TimerSchedule(1000*60*4l);

       TimerSchedule timerSchedule5=new TimerSchedule(1000*60*4l);

       TimerSchedule timerSchedule6=new TimerSchedule(1000*60*5l);

//seam中的事件机制,用来启动任务,

       events.raiseTimedEvent("createReportByYear_xzqh1", timerSchedule0);

       events.raiseTimedEvent("createReportByYear_xzqh2", timerSchedule1);

       events.raiseTimedEvent("createReportByYear_xzqh3", timerSchedule2);

       events.raiseTimedEvent("createReportByYear_xzqh4", timerSchedule3);

        events.raiseTimedEvent("createReportByYear_xzqh5", timerSchedule4);

       events.raiseTimedEvent("createReportByYear_xzqh6", timerSchedule5);

       events.raiseTimedEvent("createReportByYear_xzqh7", timerSchedule6);

    }

//具体任务

    @Observer("createReportByYear_xzqh1")

    public void createReportByYearXzqh1(){

       em.getTransaction().begin();

       createReportByYear1(XzqhVO.XCQ);

       em.getTransaction().commit();

    }

//真正的业务方法,按区来处理

    public void createReportByYear1(XzqhVO xzqh){

       xzqh=(XzqhVO)DataDictionaryAction.instance().getDataItemById(XzqhVO.class, xzqh.getId());

      

       List<Owe> oweList=em.createQuery("select h from Owe h left join fetch h.meter left join fetch h.customerInfo where h.xzqh=:xzqh order by h.meter")

       .setParameter("xzqh", xzqh)

       .getResultList();

9
5
分享到:
评论
6 楼 iamzealotwang 2008-08-19  
Javaeye的表情有问题-。- 没想发那个表情 貌似他这个表情都是混的。。
5 楼 iamzealotwang 2008-08-19  
引用

这样用jdbc写起来不是相当的麻烦吗,即使单个对象有几十个字段用jdbc做起来也是相当容易出错。

这个问题我自己想过了,具体的实现都要借助Hibernate一些方法才行,需要具体研读一下其代码才行。我试着读了读,感觉很吃力。不知道您对Hibernate代码有研究么,可否指点指点我?

引用

这样批量主要是在一些大规模的运算中使用,比如产生一个统计报表等。

这个要是不麻烦的话,您能具体的举个例子么,因为报表这个东西 我是一点概念都没有。

麻烦您了。。。
4 楼 yourenyouyu2008 2008-08-19  
我想的是 对于 one-to-many的表 采用的方法是先批量插入one端,然后再批量插入many端,这样的话也不会违反主键为空的约束。

这样用jdbc写起来不是相当的麻烦吗,即使单个对象有几十个字段用jdbc做起来也是相当容易出错。

这样批量主要是在一些大规模的运算中使用,比如产生一个统计报表等。
3 楼 iamzealotwang 2008-08-18  
您好,是这样的。我自己测试了一下单表Hibernate回调JDBC API 的操作速度是你所说的传统解决方案的6~10倍左右。
传统方法:3000ms
回调API:300ms

不过我也同意你关于多表连接情况下的批量插入看法。

我想的是 对于 one-to-many的表 采用的方法是先批量插入one端,然后再批量插入many端,这样的话也不会违反主键为空的约束。

不过我想不出来,具体的业务例子不能够采用这种批量插入策略的。其实我几乎想不出来在什么情况下需要进行批量插入,还是大规模的。 我还没有参加工作 所以没有什么相关的经验。 您能帮我举出一个例子么? 谢谢了。
2 楼 yourenyouyu2008 2008-08-16  
用JDBC基本上是误导,如果是级联对象就要增加级联对象的sql,批量selct所用的时间也就几十秒而已。百万级的数据十分钟内就可以了。
1 楼 iamzealotwang 2008-08-14  
我看书上写的是
建议批量更新删除等方法 还是用Hibernate回调JDBC的API好 因为Hibernate批量处理的时候 都是先Select出来再批量处理数据的,所以感觉你给出的这个方案好像还是没有从根本上解决这个问题。不知道我说的对不对。

相关推荐

    hibernate 单元测试批处理代码

    在这个“hibernate 单元测试批处理代码”项目中,我们将深入探讨如何使用Hibernate进行单元测试,并实现批处理操作,特别针对MySQL数据库。 首先,让我们理解Hibernate单元测试的基础。单元测试是针对软件中的最小...

    Hibernate批量处理

    这意味着每次向数据库提交的SQL批处理命令的数量为20。 - **标识符生成器限制**:如果使用“identity”作为主键生成策略,则Hibernate无法在JDBC层进行批量插入操作。 - **二级缓存管理**:在进行批量操作时,建议...

    hibernate的查询方式介绍和hibernate的批处理和连接池配置hibernate

    ### Hibernate 查询方式与批处理及连接池配置详解 #### 一、Hibernate 查询方式介绍 Hibernate 是一款优秀的 ORM(对象关系映射)框架,它能够极大地简化数据库操作,并提供多种查询方式来满足不同的业务需求。 #...

    Hibernate 简单 PPT 总结

    - "2018-7-26-Hibernate-加载策略.pptx"可能涉及了Hibernate的对象状态管理,包括延迟加载、立即加载、批处理加载等策略。 - "05-2018-7-9-Hibernate-配置数据库连接池.pptx"可能介绍了如何在Hibernate中配置和使用...

    hibernate3必要jar包

    以下是一些关于Hibernate3及其核心组件的知识点: 1. **Hibernate3简介**:Hibernate3是Hibernate项目的第三个主要版本,它提供了一种对象关系映射(ORM)解决方案,允许开发人员使用面向对象的编程模型来处理关系...

    Hibernate 中文api 等学习资料

    描述中的"hibernate orm框架api中文文档,学习资料,框架详解资料"进一步明确了这些资源的性质,即它们是关于Hibernate ORM框架的API文档、学习教程以及框架的详细解释,都是中文版本,方便中文读者学习。...

    Hibernate完整ppt教程

    - 使用批处理、懒加载、预加载、缓存策略等方法来提升Hibernate应用的性能。 - 注意避免N+1查询问题,合理设计实体关系。 11. **其他高级特性** - 自动更新/创建数据库结构、级联操作、事件监听器、拦截器等。 ...

    hibernate学习资料大全

    Hibernate 提供了多种优化手段,如批处理、预加载、缓存配置等,学习如何根据项目需求进行优化,可以显著提高系统性能。 以上只是压缩包中部分可能包含的知识点,实际学习资料可能包括教程文档、示例代码、实战...

    既可待机又可休眠的批处理文件

    在IT领域,批处理文件(Batch File)是一种在Windows操作系统中使用的简单脚本形式,它允许用户通过一组预先定义的命令来自动化任务执行。在这个特定的案例中,我们有两个批处理文件,`suspend.bat` 和 `sleep.bat`...

    Hibernate3的依赖包

    10. **增强的性能**:通过延迟加载、批处理和缓存机制,Hibernate能够在不牺牲性能的情况下提供强大的功能。 在Eclipse中使用这些依赖包,你需要按照以下步骤操作: 1. **创建项目**:首先,在Eclipse中创建一个新...

    hibernate 3.6 中文 chm

    14. **性能优化**:文档还会涵盖如何通过批处理、预加载、缓存策略等手段优化Hibernate的性能。 以上只是《Hibernate 3.6 中文 CHM》文档中部分关键知识点的概述。通过深入学习和实践,开发者可以充分利用Hibernate...

    hibernate_3.2官方开发包

    13. **查询优化**:Hibernate提供了SQL生成器,可以根据不同的数据库生成最优的SQL语句,同时支持批处理操作,提高性能。 这个“hibernate_3.2官方开发包”包含了源码、文档、示例等,可以帮助开发者深入了解...

    Hibernate框架jia包

    - 批量操作:利用批处理更新或删除,减少数据库交互次数。 -延迟加载(Lazy Loading):只在需要时加载关联对象,避免数据冗余。 8. **HQL和Criteria API**: HQL是面向对象的查询语言,类似于SQL但更接近Java。...

    Hibernate4.0,Hibernate5.2,Hibernate5.6 jar包

    此外,还增强了对批处理操作的支持,提高了性能。同时,4.0版本引入了新的查询语言HQL(Hibernate Query Language)和Criteria API,提供了更加灵活和强大的查询机制。 接下来是Hibernate5.2版本。这个版本进一步...

    hibernate aip hibernate aip

    7. **性能优化**:包括延迟加载(Lazy Loading)、批处理(Batch Processing)、缓存策略调整等,这些都可以提升Hibernate应用的性能。 8. **查询优化**:避免N+1查询问题,使用JOIN fetch提前加载关联数据,使用...

    达梦 Hibernate 方言 2.0 至 4.0

    5. **新功能支持**:随着Hibernate的进化,新版本的方言也会引入对如批处理操作、存储过程调用等新功能的支持,使开发者能更好地利用达梦数据库的高级特性。 6. **错误修复**:每个版本迭代通常会包含对已知问题的...

    hibernate4.3.11所需jar包

    9. **性能优化**: Hibernate提供了多种性能优化手段,如批处理、延迟加载、结果集缓存等,以适应不同的应用场景。 总结来说,"hibernate4.3.11所需jar包"不仅包含Hibernate的核心库,还涉及到一系列依赖的第三方库...

    Hibernate实战

    性能优化涉及查询优化、批处理、连接池配置等。事件监听器和拦截器允许在特定操作前后执行自定义逻辑,增强系统的可扩展性。 总之,《Hibernate实战》这本书全面覆盖了Hibernate的基础和进阶内容,对于想要掌握和...

    hibernate第一个hibernate

    另外,合理的配置和使用批处理、延迟加载等特性也能有效提升应用性能。 10. 扩展与进阶: Hibernate支持多种数据库,且与Spring框架结合使用可以实现更高级的功能,如事务的声明式管理、DAO模式的实现等。学习完...

Global site tag (gtag.js) - Google Analytics