简要说明: 在父级及其子级之一更新后,事务提交,会话关闭,下一级会话从L2缓存获取父级,但更新的子级从db加载。家长是版本的,孩子不是。
详细说明: 正如我所提到的,我有父母与儿童收藏。父版本已经版本化,但子版本不是我的应用程序,映射保证所有更新都通过保存父级来完成。我看到当我加载带子项的父项时,父项和大多数子项都是从L2缓存加载的,但最近更新的子项是从数据库加载的。我在ReadWriteCache细节上做了一些研究,发现在保存父和更新的子代之后,下面的代码决定不更新缓存:
public bool Put(CacheKey key, object value, long txTimestamp, object version, IComparer versionComparator, bool minimalPut)
{
bool CS$1$0000;
if (txTimestamp == -9223372036854775808L)
{
return false;
}
lock (this._lockObject)
{
if (log.IsDebugEnabled)
{
log.Debug("Caching: " + key);
}
try
{
this.cache.Lock(key);
ILockable lockable = (ILockable) this.cache.Get(key);
if ((lockable == null) || lockable.IsPuttable(txTimestamp, version, versionComparator))
{
this.cache.Put(key, new CachedItem(value, this.cache.NextTimestamp(), version));
if (log.IsDebugEnabled)
{
log.Debug("Cached: " + key);
}
return true;
}
if (log.IsDebugEnabled)
{
if (lockable.IsLock)
{
log.Debug("Item was locked: " + key);
}
else
{
log.Debug("Item was already cached: " + key);
}
}
CS$1$0000 = false;
}
finally
{
this.cache.Unlock(key);
}
}
return CS$1$0000;
}
在我的情况下,lockable不为null,但它不可放置:
public bool IsPuttable(long txTimestamp, object newVersion, IComparer comparator)
{
// we really could refresh the item if it
// is not a lock, but it might be slower
//return freshTimestamp < txTimestamp
return version != null && comparator.Compare(version, newVersion) < 0;
}
从NHibernate源代码可以看出,早期的决策是通过比较时间戳来制定的,但现在版本开始发挥作用。
现在的问题是:我应该在我的域模型中对每个实体进行版本化吗?在问题出现之前,我确信我应该只对Aggregate Roots进行版本化。
我真的希望找到解决这个问题的另一种方法,因为我的父子层次结构要深得多(不要问为什么:)),让我们说它深层次3级(实际上它更深): 根 - &GT;儿童安全&GT;如GrandChild
在仅更新GrandChild的情况下,我预计会有2个UPDATE语句:一个用于GrandChild,另一个用于Root以更新其版本。但是,如果我版本Child也会花费额外的数据库更新和往返:( ...顺便说一句,应该ping Ayende或来自NH的人 - ADO.NET批处理NH只适用于相同的实体,它不会放在同一个GrandChild,Child和Root的批量更新语句。
答案 0 :(得分:1)
您还需要在子实体上设置版本属性,而不仅仅是聚合根。我在我当前的项目中有一个类似的用例,这就是它的工作方式。我们通过实现实体的基类中的版本和流畅的nhibernate映射类的基类来管理它。
答案 1 :(得分:1)
对不起,伙计们,让你感到困惑。现在看来我已经明白了。让我们来看看ReadWriteCache类(实际上是一个策略,而不是它自己的缓存)。我们对三种方法感兴趣:Put(我在我的问题中提到过),AfterInsert和AfterUpdate:
AfterUpdate决定我们应该在L2中存储更新的实体。在非并发环境中,它取决于
public bool IsCacheInvalidationRequired
{
get { return HasFormulaProperties || (!IsVersioned && (entityMetamodel.IsDynamicUpdate || TableSpan > 1)); }
}
最后让我们谈谈Put。我们决定是否应该从L2缓存中的数据库实体存储加载。可能发生以下情况:a)如果L2没有这个(通过id)实体,我们总是存储加载的实体b)如果L2已经有这个实体,那么我们只在L2 中更新它如果实体是版本化的它的版本比现有版本更新。
希望现在我真的明白,当我创建或更新的实体出现在L2缓存中时,为什么我会看到额外的db select语句。所有这些规则都适用于ReadWriteCache。可能有人使用NonStrictReadWriteCache,但这不是我的选择。