NHibernate 可以在会话关闭后从二级缓存加载数据吗?
Can NHibernate load data from 2nd level cache after session is closed?
我正在尝试使用 NHibernate 为我的应用程序制作一些东西。我使用了很多 "dictionaries" 来存储某些对象属性的所有可能值。我试过使用二级缓存来存储那些字典数据。现在我想知道是否有办法在会话关闭后从缓存中加载所需的数据。假设这是我的代码:
public class Class1 {
public virtual int Id { get; set; }
public virtual Dic1 Dic { get; set; }
}
public class Dic1 {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
这里是映射:
<class name="Class1" table="class1">
<id name="Id" column="id">
<generator class="native" />
</id>
<!-- I want to try not to use fetch="join" here -->
<many-to-one
name="Dic"
class="Dic1"
column="dic1_id"
/>
</class>
<class name="Dic1" table="dic1">
<cache usage="read-write"/>
<id name="Id" column="id">
<generator class="native" />
</id>
<property name="Name" column="name" />
</class>
如果我在关闭会话之前获取 Class1.Dic 对象的值,NHibernate 不会将查询发送到数据库,因为该值已被一些较早的查询缓存。
但是假设我已经关闭了会话。在调试会话中,Class1.Dic 是 Dic1Proxy 类型的对象,当我尝试访问 it/it 的属性时出现异常。有没有办法在会话关闭后加载该数据?二级缓存连接到会话工厂,所以也许有办法实际将该代理转换为正确的对象?或者实际上强制始终加载这些值而不将 fetch
方法更改为 join
.
您可以在关闭会话前使用 NHibernateUtil.Initialize(class1.Dic);
。如果对象实际上不是代理或者它已经加载,它将不执行任何操作,否则它将加载它(如果缓存,则从二级缓存)。
您还可以在保持默认 select
获取模式的同时强制预先获取:在 Dic
的 many-to-one
映射上将 lazy
设置为 false
属性。加载 Class1
将立即触发 Dic
属性 的加载。如果它在二级缓存中,它应该从二级缓存中获取它。请注意,如果您查询 Class1
的列表并且它们的 Dic
属性 未被缓存,即使您启用了 batching of lazy loads,这也会导致 n+1 加载问题。 =63=]
否则,如果您不想在关闭会话之前对 Dic
属性 进行任何类型的操作,则需要更改代理实现以首先检查二级缓存如果会话已经关闭,则在失败之前。但在我看来,这需要付出太多的努力才值得。 (此外,如果实体在缓存中丢失怎么办?在这种情况下您的应用程序失败是否可以接受?)
NHibernate 允许您使用 proxyfactory.factory_class
optional setting 提供您自己的代理工厂工厂 (IProxyFactoryFactory
),前提是您使用默认的字节码提供程序。
然后您需要实现自己的 IProxyFactoryFactory
,这很可能主要是 StaticProxyFactoryFactory
的副本,其中 BuildProxyFactory
产生自定义代理工厂。
自定义代理工厂本身可能主要是 StaticProxyFactory
, with GetProxy
using a custom ILazyInitializer
at this line.
的副本
自定义惰性初始化器可能是 LiteLazyInitializer
, but with an override of Initialize
. Its implementation is here.
的副本
这是最简单的部分,直到那里,这并不像听起来那么糟糕,不需要复制那么多行代码。
现在要覆盖 Initialize
,它需要检查 Session
属性 并相应地采取行动,如果会话可用则调用它的 base implementation,或者否则尝试直接从二级缓存加载。
这里你会有更多的代码要复制,主要是LoadFromSecondLevelCache and AssembleCacheEntry。
您还需要持久化器,如果您有会话工厂就很容易获得它:sessionFactory.GetEntityPersister(EntityName)
。 (ILazyInitializer
有一个 EntityName
属性。)
正如您通过检查它们的代码所看到的,这些函数在许多点上使用了会话:
CacheMode
:已检查以确保为会话启用了缓存,您当然应该为您的用例跳过该检查。
GenerateCacheKey
:容易内联出session,见its code。
Timestamp
:使用 sessionFactory.Settings.CacheProvider.NextTimestamp()
应该可以做到。
Instantiate
:改用subclassPersister.Instantiate(id)
。 (除非你有一个应该委托给它的拦截器。)
其他调用比较麻烦
您将不得不放弃对循环引用的安全保护,因为它使用会话持久性上下文。
然后是使用会话的 Assemble
和 DeepCopy
逻辑。许多情况下只是调用它的 Factory
属性,因此根据实体的 属性 类型,实际提供工厂的虚拟会话可能会做。
如果可能,请跳过只读内容,否则您还有更多工作要做。
跳过大部分 persistenceContext
内容:即会话一级缓存。仍然有 InitializeNonLazyCollections
调用,如果您的实体有一些调用,它将缺少。
关于 AfterInitialize
,当前需要此调用来处理具有惰性属性的实体(不是实体或集合)。所以你可以跳过它。
最后,PostLoadEvent
用于实现 ILifecycle
的实体:同样,您可以跳过它,前提是您不使用 ILifecycle
.
如果您还有一些缓存集合要在没有会话的情况下从二级缓存中检索,您需要对集合类型工厂进行类似的工作,可使用 collectiontype.factory_class
进行配置,提供您自己的 ICollectionTypeFactory
生成集合类型覆盖 Initialize
, likewise duplicating the loading from second level cache.
祝你好运。
我正在尝试使用 NHibernate 为我的应用程序制作一些东西。我使用了很多 "dictionaries" 来存储某些对象属性的所有可能值。我试过使用二级缓存来存储那些字典数据。现在我想知道是否有办法在会话关闭后从缓存中加载所需的数据。假设这是我的代码:
public class Class1 {
public virtual int Id { get; set; }
public virtual Dic1 Dic { get; set; }
}
public class Dic1 {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
这里是映射:
<class name="Class1" table="class1">
<id name="Id" column="id">
<generator class="native" />
</id>
<!-- I want to try not to use fetch="join" here -->
<many-to-one
name="Dic"
class="Dic1"
column="dic1_id"
/>
</class>
<class name="Dic1" table="dic1">
<cache usage="read-write"/>
<id name="Id" column="id">
<generator class="native" />
</id>
<property name="Name" column="name" />
</class>
如果我在关闭会话之前获取 Class1.Dic 对象的值,NHibernate 不会将查询发送到数据库,因为该值已被一些较早的查询缓存。
但是假设我已经关闭了会话。在调试会话中,Class1.Dic 是 Dic1Proxy 类型的对象,当我尝试访问 it/it 的属性时出现异常。有没有办法在会话关闭后加载该数据?二级缓存连接到会话工厂,所以也许有办法实际将该代理转换为正确的对象?或者实际上强制始终加载这些值而不将 fetch
方法更改为 join
.
您可以在关闭会话前使用 NHibernateUtil.Initialize(class1.Dic);
。如果对象实际上不是代理或者它已经加载,它将不执行任何操作,否则它将加载它(如果缓存,则从二级缓存)。
您还可以在保持默认 select
获取模式的同时强制预先获取:在 Dic
的 many-to-one
映射上将 lazy
设置为 false
属性。加载 Class1
将立即触发 Dic
属性 的加载。如果它在二级缓存中,它应该从二级缓存中获取它。请注意,如果您查询 Class1
的列表并且它们的 Dic
属性 未被缓存,即使您启用了 batching of lazy loads,这也会导致 n+1 加载问题。 =63=]
否则,如果您不想在关闭会话之前对 Dic
属性 进行任何类型的操作,则需要更改代理实现以首先检查二级缓存如果会话已经关闭,则在失败之前。但在我看来,这需要付出太多的努力才值得。 (此外,如果实体在缓存中丢失怎么办?在这种情况下您的应用程序失败是否可以接受?)
NHibernate 允许您使用 proxyfactory.factory_class
optional setting 提供您自己的代理工厂工厂 (IProxyFactoryFactory
),前提是您使用默认的字节码提供程序。
然后您需要实现自己的 IProxyFactoryFactory
,这很可能主要是 StaticProxyFactoryFactory
的副本,其中 BuildProxyFactory
产生自定义代理工厂。
自定义代理工厂本身可能主要是 StaticProxyFactory
, with GetProxy
using a custom ILazyInitializer
at this line.
自定义惰性初始化器可能是 LiteLazyInitializer
, but with an override of Initialize
. Its implementation is here.
这是最简单的部分,直到那里,这并不像听起来那么糟糕,不需要复制那么多行代码。
现在要覆盖 Initialize
,它需要检查 Session
属性 并相应地采取行动,如果会话可用则调用它的 base implementation,或者否则尝试直接从二级缓存加载。
这里你会有更多的代码要复制,主要是LoadFromSecondLevelCache and AssembleCacheEntry。
您还需要持久化器,如果您有会话工厂就很容易获得它:sessionFactory.GetEntityPersister(EntityName)
。 (ILazyInitializer
有一个 EntityName
属性。)
正如您通过检查它们的代码所看到的,这些函数在许多点上使用了会话:
CacheMode
:已检查以确保为会话启用了缓存,您当然应该为您的用例跳过该检查。GenerateCacheKey
:容易内联出session,见its code。Timestamp
:使用sessionFactory.Settings.CacheProvider.NextTimestamp()
应该可以做到。Instantiate
:改用subclassPersister.Instantiate(id)
。 (除非你有一个应该委托给它的拦截器。)
其他调用比较麻烦
您将不得不放弃对循环引用的安全保护,因为它使用会话持久性上下文。
然后是使用会话的 Assemble
和 DeepCopy
逻辑。许多情况下只是调用它的 Factory
属性,因此根据实体的 属性 类型,实际提供工厂的虚拟会话可能会做。
如果可能,请跳过只读内容,否则您还有更多工作要做。
跳过大部分 persistenceContext
内容:即会话一级缓存。仍然有 InitializeNonLazyCollections
调用,如果您的实体有一些调用,它将缺少。
关于 AfterInitialize
,当前需要此调用来处理具有惰性属性的实体(不是实体或集合)。所以你可以跳过它。
最后,PostLoadEvent
用于实现 ILifecycle
的实体:同样,您可以跳过它,前提是您不使用 ILifecycle
.
如果您还有一些缓存集合要在没有会话的情况下从二级缓存中检索,您需要对集合类型工厂进行类似的工作,可使用 collectiontype.factory_class
进行配置,提供您自己的 ICollectionTypeFactory
生成集合类型覆盖 Initialize
, likewise duplicating the loading from second level cache.
祝你好运。