好吧,我看到一些帖子几乎要求同样的东西但是点数有点不同。
这是一个经典案例:我正在保存/更新实体,并且在 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以确保其工作?
谢谢。
菲利普
答案 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来查看它是否有效。