在Hibernate参考中,有几次说明
Hibernate抛出的所有异常都是致命的。这意味着你必须回滚 数据库事务并关闭当前
Session
。你不能继续 使用引发异常的Session
。
我们的一个旧版应用程序使用单个会话将文件中的许多记录更新/插入到数据库表中。每个记录更新/插入都在单独的事务中完成,然后提交(或在发生错误时回滚)。然后为下一条记录打开一个新事务,依此类推。但是在整个过程中使用相同的会话,即使在处理过程中发现了HibernateException
。我们在JBoss 4.2上使用Oracle 9i btw和Hibernate 3.24.sp1。
阅读本书中的上述内容,我意识到这种设计可能会失败。所以我重构了应用程序,为每次记录更新使用单独的会话。在使用模拟会话工厂的单元测试中,我可以验证它现在正在为每个记录更新请求新会话。到目前为止,非常好。
但是,我们发现在测试整个应用程序时无法重现会话失败(这是一个压力测试btw,还是......?)。我们考虑过关闭数据库的监听器,但我们意识到应用程序正在保持一堆连接对数据库开放,并且监听器不会影响这些连接。 (这是一个Web应用程序,每晚由调度程序激活一次,但它也可以通过浏览器激活。)然后我们尝试在应用程序处理更新时杀死数据库中的一些连接 - 这导致一些失败更新,但随后应用程序愉快地继续更新其余记录。显然,Hibernate足够聪明,可以在不破坏整个会话的情况下重新打开引擎盖下断开的连接。
所以这可能不是一个关键问题,因为我们的应用程序似乎足够强大,即使是原始形式。但是,这个问题一直困扰着我。我想知道:
HibernateException
之后,Hibernate会话在什么情况下真的变得无法使用(更新:,有什么症状)?答案 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
之后证明事情按预期工作(并且可能仍然是“意外”)会非常复杂(在技术上和功能上)。
因此,我认为正确的做法是遵循建议(或建议的替代方案,但我只是为每笔交易请求新的Session
和close
,而不是尝试证明任何事情,是否抛出异常)。
这种测试的正确用语是什么?
集成测试?稳健性测试?
答案 1 :(得分:3)
有趣的一个。最好的方法是去看看Hibernate的来源。据我所知,Session
除了以下内容外没有多少状态:
JDBCContext
和ActionQueue
一起使用。最后一点,事务本身的回滚不会以任何方式干扰会话状态,请参阅JDBCTransaction。
因此,就我一直玩的而言,Session
实际上存在错误的事务,除了第一级缓存可能稍微不准确;为了解决这个问题,您可能更愿意调用session.refresh
或session.load
来明确重新同步状态。
session.createSQLQuery("something offensive to SQL parser").executeUpdate()
- 你会得到一个完美的SQLGrammarException
- 一个破坏的事务=)
答案 2 :(得分:1)
我对此的看法是
几乎不可能知道Hibernate是否会工作,它遇到意外情况并且现在正在以未定义状态运行 - 基本上你将通过在会话上执行任何更多操作来调用UB。
想法 - 注册拦截器并在其中抛出HibernateException - 希望Hibernate不会捕获它:D
故障注入?
答案 3 :(得分:0)
创建Session的子类以进行测试。实现它以在某些特定场景中抛出异常。使用特定于测试的hibernate配置来设置hibernate以使用您的测试会话。