抛出异常后使用当前会话

时间:2014-09-11 05:42:50

标签: java spring hibernate session bulkinsert

根据Hibernate documentation,在Hibernate抛出异常后使用Session是不安全的。

  

如果Session抛出异常(包括任何SQLException),则立即回滚数据库事务,调用Session.close()并丢弃Session实例。 Session的某些方法不会使会话保持一致状态。 Hibernate抛出的异常都不能被视为可恢复的。确保通过在finally块中调用close()来关闭Session。

在我的代码中,我正在进行批量插入。我正在使用sessionFactory.getCurrentSession()方法来获取会话。我的代码是这样的。

try {
  //some code here....
  ....
  Table1Entity table1Entity = .......
  List<Table2Entity> table2Entities = .......
  Session currentSession = sessionFactory.getCurrentSession();
  for (int i = 0; i < table2Entities; i++) {
      .............
      currentSession.save(table2Entities.get(i));
      if(i % batchSize == 0 || i + 1 == table2Entities) {
        currentSession.flush();
        currentSession.clear();
    }
  }
} catch (Exception e) {
    currentSession.getTransaction().rollback();
    //currentSession.close(); //According to documentation Session should be closed here
    table1Entity.setError(true);
    currentSession.save(table1Entity);//According to documentation Session should not be used here
    .......
}

正如文档所说,在抛出异常后不应使用会话。我的问题是因为我使用currentSession,如何在catch块中保存table1Entity?我应该使用openSession()方法还是以任何其他方式打开新会话?


修改:更清楚一点,我要问的是,在现有 currentSession 关闭后,是否可以检索新的 currentSession

2 个答案:

答案 0 :(得分:0)

这是一个try..catch块。你怎么能确定在table1Entity上进行的任何处理都不会抛出Hibernate异常?如果在处理table1Entity时出现异常,那么尝试将其保存在当前会话中可能会产生更大的问题。

我的建议是将表上的处理分成单独的try..catch块,并在每个块中单独调用save。抛出异常后,尝试在catch块中的同一会话中保存事务是不安全的,并且假设table1Entity中没有问题,有时可能会违反直觉。

答案 1 :(得分:0)

如果与该数据库的连接发生异常,我建议不要写入数据库。所以基本上,我认为你的方法有严重的问题,修复它很可能对你没有好处。

在这种情况下,你应该非常小心你的批次不会抛出异常,无论如何。因此,如果发生异常,您可以假设数据库无法访问,或者无论如何都不会接受任何尝试。将错误写入其他地方(事实证明日志文件对此有用)。

我可以看到你的方法似乎是可行的情况(比如我更新所有有效的方法,我标记的其他方法。要识别“其他”我使用例外)但它旁边永远不会事实上这是一个好主意,而是“诙谐”的情况。 - 不要诙谐,不会得到回报。

而是以一种不会成为首选项的方式编写批处理,而是返回一个说“我没有工作”并正确处理的正确结果。

所以在你的情况下:

try {
  //some code here....
  ....
  Table1Entity table1Entity = .......
  List<Table2Entity> table2Entities = .......
  Session currentSession = sessionFactory.getCurrentSession();
  for (int i = 0; i < table2Entities; i++) {
      .............

      // HERE you need to make sure that this call would succeed
      currentSession.save(table2Entities.get(i));
      if(i % batchSize == 0 || i + 1 == table2Entities) {
        currentSession.flush();
        currentSession.clear();
    }
  }
  currentSession.save(table1Entity);
  currentSession.getTransaction().commit();
} catch (Exception e) {
  currentSession.getTransaction().rollback();
  log.warn("Could not do what I wanted", e);
}

要么全部工作要么根本不工作,并为你提供一个正确的堆栈跟踪信息和所有信息(日志)。

也许你想在调用之前调查一个调用是否成功。

如果您认为自己必须按照自己的意愿行事,请在其答案下阅读CodeNewbie的评论:使用setGoingToWriteBatch(true)创建table1Entry,然后在批处理setBatchWritten(true)的末尾。这样你就可以找到第一个为真的那个,但第二个不是。