对于多个事务,Hibernate FlushMode设置为Manual

时间:2013-08-21 17:06:57

标签: 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 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();
}

每10次提交的事务(i = 100)就会发生刷新。这是后台工作的一部分,需要进行大量处理。对象数量大约在20,000左右,我无法在整个过程完成之前清除会话,因为我最终在Lazy初始化异常中。我的代码工作正常,但在这里我对手动模式下刷新和提交的顺序感到困扰。这可以用更好的方式完成吗?

2 个答案:

答案 0 :(得分:0)

我在一个应用程序中工作,我们经常在这种操作中获得OptimisticLockExceptions并修复它我们只是重新批量处理。缺点是,如果您的数据库在批处理中途脱机,您仍然可能会损坏数据库。

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);
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();
}

但是,如果你有20,000个对象,那么这显然不会给你带来很好的表现(事实上,即使第一种方法也不会很好)。如果你确实有20,000个对象,那么只有你能够提高性能的唯一方法就是每个批次刷新并清除。这意味着您必须密切管理您的操作方式,以避免延迟初始化程序异常。

答案 1 :(得分:0)

你在模数中使用常量10的原因是什么?

你应该总是想出于某种原因做某事:-)我的意思是什么?

如果你想提供更好的吞吐量或者你有少量的RAM,我会提供缓存刷新和清除迭代中的每个周期,因为小事务会带来这个功能。

如果您没有此要求,则取决于您的列表大小是在最后提交还是在迭代期间提交您自己设计的方式。要注意常数10,因为它有时可能足够,有时很少,有时太多。我更愿意决定何时根据其他重要标志进行刷新,例如会话中的对象计数或占用的内存等。请注意,您不应该只关心hibernate缓存的大小,还要关心数据库事务日志。

如果你关注性能,看看无状态会话,我在几年之前做了some performance测试(.NET)