【问题标题】:Flush NHibernate whilst still allowing transaction rollback刷新 NHibernate,同时仍然允许事务回滚
【发布时间】:2010-12-18 04:14:00
【问题描述】:

我正在尝试将 NHibernate 与未使用 NHibernate 映射的旧实体一起使用。有时这意味着我需要手动将 NHibernate 数据刷新到数据库,这样当我尝试将遗留实体与 NHibernate 映射的实体连接时,我不会收到外键异常。

当这发生在需要回滚的事务中时,就会出现问题。从 NHibernate 刷新的数据不会回滚。

对此我有什么办法吗?

更新

仍然很好奇如何做到这一点 - 我不相信给出的任何一个答案都解决了这个问题。我需要调用 Flush()。问题是,如何回滚已经刷出的数据?

【问题讨论】:

  • 请告诉我为什么投反对票?当前的答案很好,但不能解决问题 - 我需要能够刷新到数据库,以便非 NHibernate 托管查询可以访问 NHibernate 中已更新的数据。如果我不能调用刷新,非 NHibernate 托管查询还能如何访问数据?想象一下,自己使用简单的 System.Data.SqlClient 调用来做这件事——这将非常简单:将一堆 SqlCommand.ExecuteUpdate() 调用包装在 TransactionScope 中,仅此而已。您可以嵌套事务、回滚它们等,一切都会按预期运行。

标签: nhibernate transactions flush


【解决方案1】:

检查这个:Force query execution without flush/commit

我似乎有同样的问题,我会刷新然后我会回滚,但一些数据会保留在数据库中。但是,我的代码中有一些部分会调用提交,并且无法回滚。考虑接受答案的代码 sn-p 作为事务、刷新、回滚和提交的正确用法,并考虑到这种模式可以扩展......

在单个工作单元中(即,我们将 Web 应用程序中的请求视为单个工作单元,并且该请求中发生的所有事情都存在于提交 onEndRequest 的单个事务中):

  1. 您只调用一次_sessionFactory.OpenSession()_session.BeginTransaction()_session.CommitTransaction()_session.CloseSession()

  2. 您可以根据需要多次调用_session.Flush()_session.RollBackTransaction(),但在提交时会自动调用 Flush()。当您需要进行查询并确保获取的数据不会过时时,您可能需要调用 Flush。

  3. 请注意,一旦提交事务被提交,之后的所有操作都不会发生在该事务上。相反,NHibernate 将在后台创建必要的事务 (http://www.nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions) 您已经在跟踪一致性和可能的​​逻辑完整性方面遇到了问题

  4. 如果您确实必须在工作单元的中间调用 commit,强烈建议此时创建一​​个新事务,以便您可以显式管理它

  5. 更好的是尝试嵌套事务据称允许部分提交;您可以回滚“根”事务,所有更改都将被还原。我还没有真正测试过 .NET 和 SQL Server 的这个特性,尽管数据库中的嵌套事务本身还有很多不足之处,而且我不知道 ADO.NET 究竟是如何使用这个特性的。

    李>

第 1 点到第 4 点已经在从 1.2 开始的所有 NHibernate 版本中进行了测试。

【讨论】:

  • 感谢您的评论。在您的测试中,您是否注意到一旦数据被刷新,通常不可能回滚事务?这就是让我头疼的原因:有时您需要在事务的一部分中刷新,但您还需要稍后能够回滚事务。 NHibernate 似乎无法做到这一点。
  • 如果它们已被刷新,它们是完全可回滚的。您必须分析您的数据库操作,也许某些东西正在调用/导致提交。我一直在进行显式和隐式刷新(例如,当您进行 hql/条件查询时)并在事务结束时进行回滚(异常或测试),没有任何问题。
【解决方案2】:

为了格式化,我允许自己在这里更新 tolism7 的答案。

  1. 使用 using 并忘记 transaction.Dispose() - transaction 将在 using 块的末尾自动变为 Dispose
  2. throw - 不要抛出 ex,因为这意味着丢弃您的堆栈跟踪(请参阅 this post,其中指出“当 .NET Framework 执行此语句时:throw ex; 它会丢弃当前函数上方的所有堆栈信息。” )

.

public void CommitChanges()
{
    using (var transaction = Session.BeginTransaction())  // <-- open scope
        try
        {
            // do something
            transaction.Commit();
        }
        catch (HibernateException)
        {
            transaction.Rollback();
            _session.Close();
            _session.Dispose();

            throw;  // <-- this way the stacktrace stays intact!
        }
}

可以在here找到这段代码的VB.NET version

【讨论】:

    【解决方案3】:

    当使用 NHibernate 的事务时,尽量避免使用 Session.Flush() 而是使用它在内部调用 session.flush() 的 transaction.Commit()。

    如果在 Commit() 期间发生错误并且需要回滚事务,可以这样解决。

    public static void CommitChanges()
    {
        ITransaction transaction = Session.BeginTransaction();
    
        try
        {
            transaction.Commit();
        }
        catch (HibernateException ex)
        {
            transaction.Rollback();
            //close and dispose session here
            throw ex;
        }
        finally
        {
            transaction.Dispose();
        }
    }
    

    现在,如果手动调用 flush() 或调用 commit() 成功通过,则无法使用 NHibernate 机制回滚事务。 特别是在调用 transaction.Commit() 命令时,NHibernate 创建的 AdoTransaction 会在 Commit() 完成后立即释放,因此您无法访问它以回滚。

    上面的代码示例允许您捕获提交期间发生的错误,然后回滚已经开始的事务。

    现在在我的测试中调用 session.Flush() 而不是在上面的示例中调用 transaction.Commit(),因为从未提交过事务,所以没有数据保存在数据库中。

    我不知道您的代码是什么样的,但是如果您以模式调用,如上面的代码示例所示,transaction.commit() 而不是 Session.Flush() 它应该为您提供一种方法实现你想要的。

    【讨论】:

    • 您的 catch(...) 部分没有添加任何功能,因为 Dispose() 已经进行了回滚(如果未提交)。
    • @taoufik 我担心您的评论无效。 NHibernate 代码中由 AdoTransaction 对象实现的 ITransaction 接口的 Dispose() 命令仅负责释放托管和非托管资源。此外,快速查看 NHibernate 源代码证明了 Dispose() 不调用 ITransaction.RollBack() 或底层 IDbTransaction 对象的 RollBack() 方法的事实......因此 catch(...) 部分完全是功能性的,并且上述代码示例所必需的。
    • 下面的帖子显示了一个代码 sn-p(来自 Reflector),它证明了 Dispose 确实调用了 Rollback:stackoverflow.com/questions/641660/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-09
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 2014-09-07
    • 2017-11-10
    • 2013-06-10
    相关资源
    最近更新 更多