关于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)
以空间换时间
针对传统方案一一解决
一般我们的对象关联都设置为延迟加载,所以循环时才去find,这样性能也是很慢的
有两种办法来解决
?
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万条),还有就是不能让这些分批在同一线程中,在同一线程中就没有分批的意义了(问题同样存在)。我们可以有两种选择
把每批用一个任务来完成,间隔执行每个任务即可,注意设置任务间隔,尽量保证同一时间只有一个任务在执行,要不内存还是会溢出的。
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();