NHibernate FlushMode自动不会在查找之前刷新

时间:2010-07-20 23:21:35

标签: nhibernate

好吧,我看到一些帖子几乎要求同样的东西但是点数有点不同。

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

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

  

此过程刷新默认情况下发生在以下几点:

     
      
  • 来自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以确保其工作?

谢谢。

菲利普

3 个答案:

答案 0 :(得分:8)

我对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;
    }
}

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

答案 1 :(得分:2)

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

似乎“最佳实践”是在显式交易中做所有事情(获取和设置):

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

有关详细信息,请参阅here

答案 2 :(得分:0)

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

为什么要将初始值设置为20,保存,然后将其更改为30?

作为一种惯例,如果您要修改会话,然后查询会话,您可能希望在这些操作之间明确刷新。你可能会有轻微的性能损失(毕竟,你不要让hibernate优化会话刷新),但你可以重新访问它是否成为一个问题。

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