Caching 会话关闭后,NHibernate能否从二级缓存加载数据?

Caching 会话关闭后,NHibernate能否从二级缓存加载数据?,caching,nhibernate,Caching,Nhibernate,我正在尝试我可以用NHibernate为我的应用程序制作的东西。我使用了相当多的“字典”来存储某些对象属性的所有可能值。我尝试过使用二级缓存将那些字典数据存储在那里。现在,我想知道是否有一种方法可以在会话关闭后从缓存加载所需的数据。假设这是我的代码: public class Class1 { public virtual int Id { get; set; } public virtual Dic1 Dic { get; set; } } public class Dic1

我正在尝试我可以用NHibernate为我的应用程序制作的东西。我使用了相当多的“字典”来存储某些对象属性的所有可能值。我尝试过使用二级缓存将那些字典数据存储在那里。现在,我想知道是否有一种方法可以在会话关闭后从缓存加载所需的数据。假设这是我的代码:

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类型的对象,当我尝试访问它/它的属性时,会出现异常。会话关闭后是否有方法加载该数据?第二级缓存连接到会话工厂,所以可能有一种方法可以将该代理实际转换为正确的对象?或者实际上强制始终加载这些值,而不将
获取
方法更改为
加入
您可以使用
NHibernateUtil.Initialize(class1.Dic),然后关闭会话。如果对象实际上不是代理,或者已经加载,它将不执行任何操作,否则它将加载它(如果缓存,则从二级缓存)

您还可以在保持
Dic
属性的
多对一映射上的默认
select
fetch模式:将
lazy
设置为
false
的同时强制进行即时抓取。加载
Class1
将立即触发加载
Dic
属性。如果它在二级缓存中,它应该从二级缓存中获取它。请注意,如果您查询
Class1
列表,并且未缓存其
Dic
属性(即使您已启用),这将导致n+1加载问题

否则,如果您不想在关闭会话之前对
Dic
属性执行任何类型的操作,则需要将其代理实现更改为在会话已关闭时首先检查二级缓存,然后失败。但在我看来,这需要做太多的工作才值得。(此外,如果实体在缓存中丢失怎么办?在这种情况下,应用程序失败是否可以接受?)

NHibernate允许您使用
proxyfactory.factory\u类提供自己的代理工厂工厂(
IProxyFactoryFactory
),前提是使用默认字节码提供程序

然后,您需要实现自己的
iproxyFactory
,这很可能是一个带有
BuildProxyFactory
的副本,生成一个自定义代理工厂

自定义代理工厂本身很可能是的一个副本,使用自定义的初始化器
ILazyInitializer

自定义惰性初始值设定项很可能是的副本,但覆盖了
Initialize
。它的实施非常困难

这是最简单的部分,在这之前,这并不像听起来那么糟糕,它不涉及复制这么多行代码

现在,对于
Initialize
的重写,它需要检查
会话
属性,并相应地执行操作,如果会话可用,则调用其,否则尝试直接从二级缓存加载

在这里,您将有更多的代码需要复制,主要是和

您还需要持久器,如果您有会话工厂:
sessionFactory.getEntityTyperMaster(EntityName)
,那么很容易获得持久器。(
ILazyInitializer
具有
EntityName
属性。)

通过检查其代码,您可以看到,这些函数在会话的许多点上使用:

  • CacheMode
    :为确保会话启用了缓存,您当然应该跳过该用例检查
  • GenerateCacheKey
    :易于内联到会话之外,请参阅
  • Timestamp
    :应该改为使用
    sessionFactory.Settings.CacheProvider.NextTimestamp()
  • 实例化
    :使用
    子类persister。实例化(id)
    。(除非您有一个应授权的拦截器。)
其他呼叫更麻烦。
您将不得不放弃对循环引用的安全保护,因为循环引用使用会话持久性上下文。
然后是使用会话的
Assemble
DeepCopy
逻辑。许多情况下,只需调用其
工厂
属性,因此根据实体的属性类型,只需实际提供工厂的虚拟会话就可以了。
如果可能,请跳过只读内容,否则您还有更多的工作要做。
跳过大部分的
persistenceContext
内容:这是会话一级缓存。但是,如果您的实体有一些调用,则仍然缺少
InitializeNonLazyCollections
调用。
关于
AfterInitialize
,当前需要此调用来处理具有惰性属性的实体(不是实体或集合)。因此,您可以跳过它。
最后,
PostLoadEvent
用于实现
ILifecycle
的实体:同样,您可以跳过它,只要您不使用
ILifecycle

如果您还有一些缓存的集合要在没有会话的情况下从二级缓存中检索,则需要使用集合类型工厂执行类似的工作,可以使用
collectiontype.factory\u类
进行配置,提供您自己的
ICollectionTypeFactory
生成集合类型覆盖,同样重复


如果你试试这个,祝你好运。

我不太确定。我的理解是二级缓存是维护的