代码之家  ›  专栏  ›  技术社区  ›  jfneis

NHIBERNATE冲洗模式自动未冲洗

  •  6
  • jfneis  · 技术社区  · 16 年前

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

    这是一个经典案例:我正在保存/更新一个实体,并且 同一会议 ,我正在尝试从数据库(使用criteria/find/enumerable/etc)中获取flushmode=auto。问题是: nhibernate没有在查询之前刷新更新 ,所以我从数据库中得到不一致的数据。

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

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

    • 一些调用 find()或Enumerable()的
    • 来自nhibernate.itransaction.commit()。
    • 来自isession.flush()。

    大胆的“一些呼吁”清楚地表明,NH完全没有责任。但是,IMO,我们在这里有一个一致性问题,因为DOC还指出:

    除了在explicity flush()中,对于会话何时执行ADO.NET调用没有绝对的保证,只有它们的执行顺序。然而, nhibernate保证isession.find(..)方法不会返回过时的数据,也不会返回错误的数据。

    所以,如果我使用createQuery(find replacement)并过滤属性值为20的实体,那么nh可以 不是 返回值为30的实体,对吗?但事实上,这就是发生的事情,因为冲洗不会在应该的时候自动发生。

    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();
    }
    

    毕竟:我是不是搞错了,这是一个bug,还是仅仅是一个不可预测的特性,所以每个人都必须调用flush来保证它的工作?

    谢谢您。

    菲利佩

    3 回复  |  直到 11 年前
        1
  •  8
  •   Jamie Ide    16 年前

    我不太熟悉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;
        }
    }
    

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

        2
  •  2
  •   David Lynch    16 年前

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

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

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

    here 为了一堆细节。

        3
  •  0
  •   hvgotcodes    16 年前

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

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

    实际上,如果您要修改会话,然后查询会话,那么您可能需要在这些操作之间显式刷新。您可能会受到轻微的性能影响(毕竟,您不会让Hibernate优化会话刷新),但如果出现问题,您可以重新访问。

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