我有NHibernate(NHibernate.Linq和Fluent NHibernate)设置了查询缓存。一切正常,直到我做session.Save(new Widget())
(即SQL INSERT
)。在此之后,该类型Widget
上的所有查询都会错过查询缓存。其他实体类型的查询缓存得很好。
using (ISession session = MySessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
// this INSERT screws things up
var widget = new Widget {Name = "Foo"};
session.Save(widget);
var query = (from w in session.Query<Widget>().Cacheable()
where w.Name == "Bar"
select w);
var fetched1 = query.FirstOrDefault();
var fetched2 = query.FirstOrDefault(); // miss?!
transaction.Commit();
}
}
如果我开始新的Transaction
,则问题仍然存在。如果我开始新的Session
,问题就会消失。这似乎有点奇怪,因为我的理解是每个SessionFactory
(不是Session
)重置二级缓存。
我认为这不重要,但我使用的是HashtableCacheProvider
,因为我现在正在测试。
答案 0 :(得分:6)
您所描述的行为是正确的(more here)。
更新时间戳缓存在您提交之前不会更新 交易!这是为了确保您不会读取“未提交的内容” 值“来自缓存。
每当我们在Cache中获得的type
发生变化时 - 缓存数据都是陈旧的...直到提交完整的事务。
想象一下,您已缓存此过滤器的结果:
var query = (from w in session.Query<Widget>().Cacheable()
where w.Name == "B*" // all names starting with B
select w);
稍后会添加新的Widget:
var widget = new Widget {Name = "Brigitte"};
session.Save(widget);
// explicit Flush is not needed,
// but then, until Commit(), query will never return Brigitte
session.Flush(); // to immediately execute INSERT
如果查询仍然被缓存,Brigitte将永远不会出现......
在Transaction中,使用FirstOrDefault()的查询会立即执行 - 写操作可能会等待Flush on Commit。
由于Transaction,所有包含的操作(insert,udpate,select)都无法从缓存中获益,因为只有Transaction作为批处理才有意义。因此,在调用commit之前,不能使用缓存。
可以在此处找到许多详细且非常有用的信息:First and Second Level caching in NHibernate
每当写入一个表时,时间戳缓存都会更新,但是会以一种棘手的方式:
- 当我们执行实际写入时,我们会在将来的某个位置写入缓存中的值。所以所有的查询都达到了 缓存现在找不到它,然后点击DB获取新数据。 由于我们处于交易中,他们会等到我们 完成交易。如果我们使用低隔离级别,和 另一个线程/机器试图将旧结果放回到 缓存,它不会成立,因为更新时间戳是进入的 未来。
- 当我们对事务执行提交时,我们使用当前值更新时间戳缓存。