如何打破Hibernate会话?

时间:2010-04-02 09:11:53

标签: java hibernate exception testing session

在Hibernate参考中,有几次说明

  

Hibernate抛出的所有异常都是致命的。这意味着你必须回滚   数据库事务并关闭当前Session。你不能继续   使用引发异常的Session

我们的一个旧版应用程序使用单个会话将文件中的许多记录更新/插入到数据库表中。每个记录更新/插入都在单独的事务中完成,然后提交(或在发生错误时回滚)。然后为下一条记录打开一个新事务,依此类推。但是在整个过程中使用相同的会话,即使在处理过程中发现了HibernateException。我们在JBoss 4.2上使用Oracle 9i btw和Hibernate 3.24.sp1。

阅读本书中的上述内容,我意识到这种设计可能会失败。所以我重构了应用程序,为每次记录更新使用单独的会话。在使用模拟会话工厂的单元测试中,我可以验证它现在正在为每个记录更新请求新会话。到目前为止,非常好。

但是,我们发现在测试整个应用程序时无法重现会话失败(这是一个压力测试btw,还是......?)。我们考虑过关闭数据库的监听器,但我们意识到应用程序正在保持一堆连接对数据库开放,并且监听器不会影响这些连接。 (这是一个Web应用程序,每晚由调度程序激活一次,但它也可以通过浏览器激活。)然后我们尝试在应用程序处理更新时杀死数据库中的一些连接 - 这导致一些失败更新,但随后应用程序愉快地继续更新其余记录。显然,Hibernate足够聪明,可以在不破坏整个会话的情况下重新打开引擎盖下断开的连接。

所以这可能不是一个关键问题,因为我们的应用程序似乎足够强大,即使是原始形式。但是,这个问题一直困扰着我。我想知道:

  1. 在抛出HibernateException之后,Hibernate会话在什么情况下真的变得无法使用(更新:,有什么症状)?
  2. 如何在测试中重现这一点(更新:最好是集成,而不是单元测试)?
  3. (这种测试的正确用语是什么?)

4 个答案:

答案 0 :(得分:4)

  

在抛出HibernateException后,Hibernate会话在什么情况下真的变得无法使用?

有趣的问题。我很确定我已经看到在这样的异常之后会话仍在“工作”的情况。问题可能是这不保证。我需要再多挖一点。

更新:从Hibernate论坛中的Hibernate开发人员引用this message

  

当发生任何异常时,应该抛弃hibernate会话。它可以相对于数据库保持不一致状态。对于只读会话,情况并非如此,因为它不使用任何会话级缓存。或者,您可以在发生异常时清除会话级缓存(但我个人并不喜欢这种方法,因为它不是一个干净的解决方案,它可能导致未来的问题和问题)。

所以问题不是真的,如果Session是“技术上可用的”,问题是它可能不会按预期运行。你显然不希望这样。

  

如何在测试中重现这一点?

您可能故意违反导致子类被抛出的条件,例如通过违反完整性约束来获取ConstraintViolationException。那样的事情:

Session session = HibernateUtil.getCurrentSession();

try {
    session.beginTransaction();    
    // execute some code that violates a unique constraint
    ...
    session.getTransaction().commit();  
} catch (HibernateException e) {
    session.getTransaction().rollback();
}

// keep using the session
try {
    session.beginTransaction();    
    ...
    session.getTransaction().commit();  
} catch (HibernateException e) {
    session.getTransaction().rollback();
}
// do we reach this point?

但我不确定这会证明什么。我的意思是,如果会话仍然有效,我们只能对这个特例进行总结。

更新:忘记我的建议,它不会证明什么。实际上,我认为在HibernateException之后证明事情按预期工作(并且可能仍然是“意外”)会非常复杂(在技术上和功能上)。 因此,我认为正确的做法是遵循建议(或建议的替代方案,但我只是为每笔交易请求新的Sessionclose,而不是尝试证明任何事情,是否抛出异常)。

  

这种测试的正确用语是什么?

集成测试?稳健性测试?

答案 1 :(得分:3)

有趣的一个。最好的方法是去看看Hibernate的来源。据我所知,Session除了以下内容外没有多少状态:

最后一点,事务本身的回滚不会以任何方式干扰会话状态,请参阅JDBCTransaction

因此,就我一直玩的而言,Session实际上存在错误的事务,除了第一级缓存可能稍微不准确;为了解决这个问题,您可能更愿意调用session.refreshsession.load来明确重新同步状态。

编辑:测试JDBC异常的最快方法可能是session.createSQLQuery("something offensive to SQL parser").executeUpdate() - 你会得到一个完美的SQLGrammarException - 一个破坏的事务=)

答案 2 :(得分:1)

我对此的看法是

  1. 几乎不可能知道Hibernate是否会工作,它遇到意外情况并且现在正在以未定义状态运行 - 基本上你将通过在会话上执行任何更多操作来调用UB。

  2. 想法 - 注册拦截器并在其中抛出HibernateException - 希望Hibernate不会捕获它:D

  3. 故障注入?

答案 3 :(得分:0)

创建Session的子类以进行测试。实现它以在某些特定场景中抛出异常。使用特定于测试的hibernate配置来设置hibernate以使用您的测试会话。