Java 对于多个事务,Hibernate FlushMode设置为手动

Java 对于多个事务,Hibernate FlushMode设置为手动,java,performance,hibernate,Java,Performance,Hibernate,这是我的密码: HibernateUtil.pushFlushMode(FlushMode.MANUAL); getCurrSess().beginTransaction(); try { for(i=0; i<list.size(); i++) { Obj dummy = list.get(i); // Here Multiple things can happen like selects, save, update and deletes of

这是我的密码:

HibernateUtil.pushFlushMode(FlushMode.MANUAL);
getCurrSess().beginTransaction();
try {
    for(i=0; i<list.size(); i++) {
        Obj dummy = list.get(i);
        // Here Multiple things can happen like selects, save, update and deletes of different objects which are related to dummy object....
        if (i > 0 && (i % 10 == 0)) {
            getCurrSess().getTran().commit();
            getCurrSess().beginTransaction();
            if (i % BATCH_SIZE == 0) {
                getCurrSess().flush();
                Thread.sleep(20);
            }
        }
    } 
} catch(Exception e) {
    getCurrSess().getTran().rollback();
} finally {
    HibernateUtil.popFlushMode();
}
HibernateUtil.pushFlushMode(FlushMode.MANUAL);
getCurrSess().beginTransaction();
试一试{
对于(i=0;i0&&(i%10==0)){
getCurrSess().getTran().commit();
getCurrSess().beginTransaction();
如果(i%批次大小==0){
getCurrSess().flush();
睡眠(20);
}
}
} 
}捕获(例外e){
getCurrSess().getTran().rollback();
}最后{
HibernateUtil.popFlushMode();
}

每10个提交的事务(i=100)进行一次刷新。这是后台工作的一部分,需要大量处理。对象的数量在20000左右变化,在整个过程完成之前,我无法清除会话,因为我最终遇到了延迟初始化异常。我的代码工作得很好,但这里我对手动模式下的刷新和提交的顺序感到烦恼。这能以更好的方式完成吗?

我在一个应用程序上工作,在这种操作中,我们经常会遇到OptimisticLockException,为了修复它,我们只需重新运行批处理。缺点是,如果您的数据库在批处理的中途脱机,您仍然可能会得到损坏的数据库

HibernateUtil.pushFlushMode(FlushMode.MANUAL);
try {
  int batchIndex = 0;
  int numAttempts = 0;
  while (batchIndex < list.size()) {
    listEnd = batchIndex+BATCH_SIZE;
    if(listEnd > list.size()) {
      listEnd = list.size();
    }
    ArrayList<> sublist = new ArrayList<>(list.subList(batchIndex, batchIndex+BATCH_SIZE));
    getCurrSess().beginTransaction();
    try {
      for(Object obj : list) {
        //Here multiple things can happen like selects, save, update, and deletes
      }
      getCurrSess().flush();
      getCurrSess().commit();
      batchIndex += BATCH_SIZE;
    } catch (Exception e) {
      getCurrSess().getTran().rollback();
      numAttempts++;
      if(numAttempts == MAX_ATTEMPTS) {
        throw new RuntimeException("Exceeded maximum number of attempts for batch operation, database is corrupted.");
      }
    }
  }
} finally {
  HibernateUtil.popFlushMode();
}
HibernateUtil.pushFlushMode(FlushMode.MANUAL);
试一试{
int batchIndex=0;
int numatempts=0;
while(batchIndexlist.size()){
listEnd=list.size();
}
ArrayList sublist=新的ArrayList(list.sublist(batchIndex,batchIndex+批次大小));
getCurrSess().beginTransaction();
试一试{
对于(对象对象:列表){
//在这里可以发生多种情况,如选择、保存、更新和删除
}
getCurrSess().flush();
getCurrSess().commit();
batchIndex+=批次大小;
}捕获(例外e){
getCurrSess().getTran().rollback();
numtempts++;
if(numtempts==最大尝试次数){
抛出新的RuntimeException(“超出了批处理操作的最大尝试次数,数据库已损坏。”);
}
}
}
}最后{
HibernateUtil.popFlushMode();
}
如果您绝对不能容忍损坏的数据库,则必须使用单个事务:

HibernateUtil.pushFlushMode(FlushMode.MANUAL);
try {
    getCurrSess().beginTransaction();
    try {
      for(int i = 0; i < list.size(); i++) {
        Object obj = list.get(i);
        //Here multiple things can happen like selects, save, update, and deletes
        if(i % BATCH_SIZE == 0) {
          getCurrSess().flush();
        }
      }
      getCurrSess().commit();
    } catch (Exception e) {
      throw new RuntimeException("Error occurred during batch.  Batch aborted.");
    }
  }
} finally {
  HibernateUtil.popFlushMode();
}
HibernateUtil.pushFlushMode(FlushMode.MANUAL);
试一试{
getCurrSess().beginTransaction();
试一试{
对于(int i=0;i

但是很明显,如果您有20000个对象,那么这不会给您带来很好的性能(事实上,即使是第一种方法也不会很好)。如果确实有20000个对象,那么提高性能的唯一方法就是对每个批进行刷新和清除。这意味着您必须密切管理您的操作方式,以避免惰性初始值设定项异常。

为什么在模中使用常量
10

你应该总是想做某事,因为有一个原因:-)我的意思是什么

如果您想要提供更好的吞吐量或者您有少量的RAM,我将提供缓存刷新&在迭代中的每个周期清除,因为小事务带来了这个特性

若您并没有这个要求,那个么这取决于列表的大小,是在迭代结束时提交,还是在迭代过程中提交,以及您试图如何设计自己。注意常量
10
,因为有时它可能足够了,有时太少,有时太多。我更愿意根据其他重要标志来决定何时进行刷新,如会话中的对象数或占用的内存等。请注意,您不仅应该关心hibernate缓存的大小,还应该关心数据库事务日志


如果您关心性能,请查看无状态会话,我在几年前就做过测试(.NET)

为什么不在每次刷新时提交?如果你还没有完成刷新,那么提交就没有意义。此外,即使这样做了,回滚也不会正常工作。如果您已经处理了一半的批处理,并且遇到错误,那么它只会在上次提交后回滚,这意味着您的批处理的前一半仍将提交到数据库。第一种方法看起来很棒,关于第二种方法,事务在整个期间保持打开状态,并且可能会锁定与操作/流程相关的所有表/行,直到您提交为止。最终可能会陷入死锁。仅供参考:我们使用Oracle和MySQL作为后端数据库。不幸的是,你不能既有蛋糕又有蛋糕。如果要对整个批次进行自动回滚管理,则必须对整个批次进行1个事务。如果您需要短事务,那么您必须对已提交的事务实施手动回滚/清理过程,或者对部分处理的批进行实时处理。模块10背后的原因是保持事务持续时间短。在此之前,它是FlushMode.AUTO,当我的上述工作被触发时,所有登录的用户都会在每个其他用例中看到明显的延迟,因为所有表中都有行锁,因此我提出了上述解决方案。好吧,我会在这个周期的每个迭代中都这样做。您的明显要求是保持事务较小,因此我将在每次迭代中提供事务。请注意,一旦您提供了将业务事务分解为小型事务的小型事务,您就需要以编程方式处理业务事务的事务回滚—您的作业执行,可能需要使用。换句话说,一旦您的任何一个小事务失败,您将能够收回作业执行粒度中的所有更改