【问题标题】:How to break a Hibernate session?如何打破休眠会话?
【发布时间】:2011-02-03 16:25:35
【问题描述】:

在 Hibernate 参考中,多次声明

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

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

看了上面的书,我意识到这个设计可能会失败。所以我重构了应用程序,为每个记录更新使用单独的会话。在使用模拟会话工厂的单元测试中,我可以验证它现在是否为每个记录更新请求一个新会话。到目前为止,一切顺利。

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

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

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

【问题讨论】:

    标签: java hibernate exception testing session


    【解决方案1】:

    在什么情况下 Hibernate 会话在引发 HibernateException 后真正变得不可用?

    有趣的问题。我很确定我已经看到在出现此类异常后会话仍在“工作”的情况。问题可能是这不能保证。我需要进一步挖掘这个。

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

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

    所以问题不在于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,无论是否抛出异常)。

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

    集成测试?稳健性测试?

    【讨论】:

      【解决方案2】:

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

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

      就我所玩的而言,Session 实际上在错误事务中幸存下来,只是一级缓存可能稍微不准确;为了解决这个问题,您可能更喜欢调用session.refreshsession.load 来显式地重新同步状态。

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

      【讨论】:

        【解决方案3】:

        我的看法是这样的

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

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

        3. 故障注入?

        【讨论】:

        • 关于 1),这也是我的理解 - 所以我很高兴我让我们的应用程序更加健壮。直接的问题是,如何在独立测试中验证这一点? 2) 听起来很有趣,因为这可以在不修改应用程序本身的情况下完成。
        【解决方案4】:

        创建 Session 的子类进行测试。实现它以在某些特定情况下抛出异常。使用测试特定的休眠配置,它设置休眠以使用您的测试会话。

        【讨论】:

        • 这确实会引发 HibernateException,但不会影响 Hibernate 会话本身的状态。换句话说,这只是一个手工实现的模拟 Session。
        • 也许我不明白你在找什么。您正在寻找一种可靠的方法将休眠状态置于损坏(例如未定义)状态?然后呢?
        • 我正在寻找一种可靠的方法将 Hibernate 会话置于中断状态,以便我们的应用程序的旧版本(跨事务重用相同会话)失败,而新版本(为每个事务获取一个新会话)通过测试。
        • k,那么,我的解决方案怎么解决不了呢?创建您的自定义测试会话以具有“损坏”标志。一旦设置,各种方法只会不断抛出异常。旧代码将失败。新代码将获得一个新会话并成功。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-27
        相关资源
        最近更新 更多