【问题标题】:NHibernate FlushMode Auto Not Flushing Before FindNHibernate FlushMode Auto Not Flushing Before Find
【发布时间】:2010-07-20 23:21:34
【问题描述】:

好吧,我看到一些帖子问的问题几乎相同,但要点有点不同。

这是一个经典案例:我正在保存/更新一个实体,并且在 SAME SESSION 中,我试图从数据库中获取它们(使用条件/查找/可枚举/等) 与 FlushMode = Auto。问题是:NHibernate 在查询之前没有刷新更新,所以我从数据库中得到了不一致的数据。

“足够公平”,有些人会说,正如文档所述:

这个过程,flush,默认发生在以下几点:

  • 来自 Find() 或 Enumerable() 的一些调用
  • 来自 NHibernate.ITransaction.Commit()
  • 来自 ISession.Flush()

粗体的“一些调用”清楚地表明 NH 完全没有责任。但是,IMO 在这里存在一致性问题,因为文档还指出:

除了显式 Flush() 之外,绝对不能保证 Session 何时执行 ADO.NET 调用,只能保证它们的执行顺序。但是,NHibernate 确实保证 ISession.Find(..) 方法永远不会返回过时的数据;他们也不会返回错误的数据。

因此,如果我使用 CreateQuery(查找替换)并过滤属性值为 20 的实体,NH 可能返回值为 30 的实体,对吗?但事实上这就是发生的事情,因为 Flush 不会在它应该自动发生的时候自动发生。

public void FlushModeAutoTest()
{
    ISession session = _sessionFactory.OpenSession();
    session.FlushMode = FlushMode.Auto;

    MappedEntity entity = new MappedEntity() { Name = "Entity", Value = 20 };
    session.Save(entity);

    entity.Value = 30;
    session.SaveOrUpdate(entity);

    // RETURNS ONE ENTITY, WHEN SHOULD RETURN ZERO
    var list = session.CreateQuery("from MappedEntity where Value = 20").List<MappedEntity>();

    session.Flush();
    session.Close();
}

毕竟:我是不是弄错了,是一个错误还是只是一个不可预测的功能,所以每个人都必须调用 Flush 来确保它的工作?

谢谢。

菲利普

【问题讨论】:

    标签: nhibernate


    【解决方案1】:

    我对 NHibernate 源代码不是很熟悉,但是 2.1.2.GA 版本中的 ISession 实现中的这个方法可能会回答这个问题:

    /// <summary>
    /// detect in-memory changes, determine if the changes are to tables
    /// named in the query and, if so, complete execution the flush
    /// </summary>
    /// <param name="querySpaces"></param>
    /// <returns></returns>
    private bool AutoFlushIfRequired(ISet<string> querySpaces)
    {
        using (new SessionIdLoggingContext(SessionId))
        {
            CheckAndUpdateSessionStatus();
            if (!TransactionInProgress)
            {
                // do not auto-flush while outside a transaction
                return false;
            }
            AutoFlushEvent autoFlushEvent = new AutoFlushEvent(querySpaces, this);
            IAutoFlushEventListener[] autoFlushEventListener = listeners.AutoFlushEventListeners;
            for (int i = 0; i < autoFlushEventListener.Length; i++)
            {
                autoFlushEventListener[i].OnAutoFlush(autoFlushEvent);
            }
            return autoFlushEvent.FlushRequired;
        }
    }
    

    我认为这意味着自动刷新只会保证事务内部的一致性,这是有道理的。尝试使用事务重写您的测试,我很好奇这是否能解决问题。

    【讨论】:

    • tks 上帝,你是对的! :) 在代码块周围使用显式事务解决了这个问题。我们可以使用标准、创建查询等等,提交更改并且查询结果通过测试。再次感谢你!我会建议 NH 团队更新文档并明确说明自动刷新功能仅在显式事务中有效,希望这有助于未来的疑问。
    • 不客气。好问题,直到现在我才明白。
    【解决方案2】:

    如果您考虑一下,您示例中的查询必须始终转到数据库。会话不是数据库中所有记录的完整缓存。因此,可能磁盘上还有其他值为 20 的实体。而且由于您没有提交()事务或刷新()会话 NH 无法知道您要查询哪个“视图”(DB | 会话)。

    似乎“最佳实践”是在显式事务中执行所有操作(获取和设置):

    using(var session = sessionFactory.OpenSession()) 
    using(var tx = session.BeginTransaction()) 
    { 
        // execute code that uses the session 
        tx.Commit(); 
    }
    

    查看here了解更多详情。

    【讨论】:

    • 我们同意会话不是一个完整的数据库缓存,而且,显然,NH 将查询数据库并且会有其他记录。交易来了:NH 的文档说 NH 会在会话中处理这些数据并自动提交它。我同意提交 tx 或刷新会话会使其正常工作,但是我们可以推断文档已过时吗?或者比这更糟糕:我们可以推断它曾经有效但现在不再有效了吗?
    【解决方案3】:

    管理和调整休眠是一种艺术形式。

    为什么要设置一个初始值20,保存,然后改成30?

    作为实践,如果您要修改会话,然后查询会话,您可能希望在这些操作之间显式刷新。您可能会受到轻微的性能影响(毕竟,您不会让 hibernate 优化会话刷新),但如果它成为问题,您可以重新考虑。

    您引用了“session.find 方法永远不会返回陈旧数据”。我会修改您的代码以使用 find 而不是 createQuery 来查看它是否有效。

    【讨论】:

    • 我正在设置和更改以测试行为,我的真实代码看起来不像这样。我正在使用 CreateQuery,因为 Find 已被弃用,而 CreateQuery 是它的建议替换。
    猜你喜欢
    • 1970-01-01
    • 2014-09-05
    • 2011-03-17
    • 1970-01-01
    • 2018-12-26
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    相关资源
    最近更新 更多