简体   繁体   English

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

[英]NHibernate FlushMode Auto Not Flushing Before Find

All right, I've seen some posts asking almost the same thing but the points were a little bit different. 好吧,我看到一些帖子几乎要问同样的事情但是点数有点不同。

This is a classic case: I'm saving/updating an entity and, within the SAME SESSION , I'm trying to get them from the database (using criteria/find/enumerable/etc) with FlushMode = Auto. 这是一个经典案例:我正在保存/更新一个实体,并且在同一个会话中 ,我试图通过FlushMode = Auto从数据库中获取它们(使用criteria / find / enumerable / etc)。 The matter is: NHibernate isn't flushing the updates before querying , so I'm getting inconsistent data from the database. 问题是: NHibernate在查询之前没有刷新更新 ,所以我从数据库中获取了不一致的数据。

"Fair enough", some people will say, as the documentation states: 有些人会说,“足够公平”,正如文件所述:

This process, flush, occurs by default at the following points: 此过程刷新默认情况下发生在以下几点:

  • from some invocations of Find() or Enumerable() 来自Find()或Enumerable()的一些调用
  • from NHibernate.ITransaction.Commit() 来自NHibernate.ITransaction.Commit()
  • from ISession.Flush() 来自ISession.Flush()

The bold "some invocations" clearly says that NH has no responsibility at all. 大胆的“一些调用”清楚地表明NH根本没有责任。 IMO, though, we have a consistency problem here because the doc also states that: 但是,IMO在这里遇到了一致性问题,因为该文档还指出:

Except when you explicity Flush(), there are absolutely no guarantees about when the Session executes the ADO.NET calls, only the order in which they are executed. 除非明确表示Flush(),否则绝对无法保证Session何时执行ADO.NET调用,只保证执行它们的顺序。 However, NHibernate does guarantee that the ISession.Find(..) methods will never return stale data; 但是, NHibernate确保ISession.Find(..)方法永远不会返回过时的数据; nor will they return the wrong data. 他们也不会返回错误的数据。

So, if I'm using CreateQuery (Find replacement) and filtering for entities with property Value = 20, NH may NOT return entities with Value = 30, right? 因此,如果我使用CreateQuery(查找替换)并过滤属性值为20的实体,NH可能不会返回值为30的实体,对吧? But that's what happens in fact, because the Flush is not happening automatically when it should. 但这就是事实上发生的事情,因为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();
}

After all: am I getting it wrong, is it a bug or simply a non predictable feature so everybody have to call Flush to assure its work? 毕竟:我错了,它是一个错误还是一个不可预测的功能所以每个人都必须打电话给Flush以确保它的工作?

Thank you. 谢谢。

Filipe 菲利佩

I'm not very familiar with the NHibernate source code but this method from the ISession implementation in the 2.1.2.GA release may answer the question: 我对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;
    }
}

I take this to mean that auto flush will only guarantee consistency inside a transaction, which makes some sense. 我认为这意味着自动刷新只能保证事务内部的一致性,这是有道理的。 Try rewriting your test using a transaction, I'm very curious if that will fix the problem. 尝试使用事务重写您的测试,我很好奇,如果这将解决问题。

If you think about it, the query in your example must always go to the db. 如果您考虑一下,示例中的查询必须始终转到数据库。 The session is not a complete cache of all records in the db. 该会话不是db中所有记录的完整缓存。 So there could be other entities with the value of 20 on disk. 因此,磁盘上可能存在值为20的其他实体。 And since you didn't commit() a transaction or flush() the session NH has no way to know which "view" you want to query (DB | Session). 由于您没有提交()事务或flush(),因此会话NH无法知道您要查询哪个“视图”(DB | Session)。

It seems like the "Best Practice" is to do everything (gets & sets) inside of explicit transactions: 似乎“最佳实践”是在显式事务中做所有事情(获取和设置):

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

See here for a bunch of details. 在这里查看一堆细节。

managing and tuning hibernate is an artform. 管理和调整hibernate是一种艺术形式。

why do you set an initial value of 20, save, then change it to 30? 为什么要设置初始值20,保存,然后将其更改为30?

As a matter of practice, if you are going modify the session, then query the session, you might want to explicitly flush between those operations. 实际上,如果您要修改会话,然后查询会话,您可能希望在这些操作之间显式刷新。 You might have a slight performance hit (after all, you then don't let hibernate optimize session flushing), but you can revisit if it becomes a problem. 您可能会有轻微的性能损失(毕竟,您不要让hibernate优化会话刷新),但如果它成为问题,您可以重新访问。

You quoted that "session.find methods will never return stale data". 您引用“session.find方法永远不会返回陈旧数据”。 I would modify your code to use a find instead of createQuery to see if it works. 我会修改你的代码,使用find而不是createQuery来查看它是否有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM