我正在尝试使用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类型的对象,当我尝试访问它/它的属性时,我得到一个异常。会话关闭后有没有办法加载这些数据?二级缓存连接到会话工厂,所以也许有办法将该代理实际转换为正确的对象?或者实际上强制要始终加载这些值而不将fetch
方法更改为join
。
答案 0 :(得分:1)
您可以在关闭会话前使用NHibernateUtil.Initialize(class1.Dic);
。如果对象实际上不是代理或者它已经加载,它将不会执行任何操作,否则它将加载它(如果缓存,则从二级缓存加载)。
您还可以在保持默认select
获取模式的同时强制进行预先获取:在lazy
属性的false
映射上将many-to-one
设置为Dic
。加载Class1
将立即触发Dic
属性的加载。如果它在二级缓存中,它应该从二级缓存中获取它。请注意,如果您查询Class1
列表并且未缓存其Dic
属性,即使您已启用batching of lazy loads,也会导致n + 1次加载问题。
否则,如果您不希望在关闭会话之前对Dic
属性进行任何操作,则需要更改其代理实现,以便在失败之前首先检查二级缓存,如果会话是已经关闭。但在我看来,这需要太多的工作才值得。 (此外,如果实体在缓存中丢失了怎么办?在这种情况下你的应用程序是否可以失败?)
NHibernate允许您使用IProxyFactoryFactory
optional setting提供自己的代理工厂工厂(proxyfactory.factory_class
),前提是您使用默认字节码提供程序。
然后,您需要实现自己的IProxyFactoryFactory
,这可能主要是StaticProxyFactoryFactory
的副本,BuildProxyFactory
会产生自定义代理工厂。
自定义代理工厂本身可能主要是StaticProxyFactory
的副本,GetProxy
使用自定义ILazyInitializer
at this line。
自定义延迟初始化程序反过来可能是LiteLazyInitializer
的副本,但覆盖了Initialize
。其实施是here。
这是为了简单的部分,直到那里,它没有听起来那么糟糕,这不涉及复制如此多的代码行。
现在要覆盖Initialize
,需要检查Session
属性并采取相应措施,如果会话可用,则调用base implementation或尝试直接从否则为二级缓存。
在这里,您需要复制更多代码,主要是LoadFromSecondLevelCache和AssembleCacheEntry。
您还需要persister,如果您有会话工厂,则很容易获得:sessionFactory.GetEntityPersister(EntityName)
。 (ILazyInitializer
具有EntityName
属性。)
正如您通过检查其代码所看到的,这些函数在会话的许多点使用:
CacheMode
:检查确保为会话启用了缓存,您肯定应该跳过对您的用例的检查。GenerateCacheKey
:易于内联,请参阅its code。Timestamp
:使用sessionFactory.Settings.CacheProvider.NextTimestamp()
代替它。Instantiate
:改为使用subclassPersister.Instantiate(id)
。 (除非你有一个应该委派给它的拦截器。)其他电话更麻烦
您将不得不放弃循环引用中的保管,因为它使用会话持久性上下文
然后是使用会话的Assemble
和DeepCopy
逻辑。许多情况只是调用它的Factory
属性,因此根据实体的属性类型,实际供应工厂的虚拟会话可能会这样做。
如果可能的话,略过只读的东西,否则你还有更多的工作要做
跳过大多数persistenceContext
内容:即会话第一级缓存。如果您的实体有一些,那么InitializeNonLazyCollections
电话仍然缺少
关于AfterInitialize
,当前需要此调用来处理具有延迟属性的实体(不是实体或集合)。所以你可以跳过它。
最后,PostLoadEvent
用于实施ILifecycle
的实体:再次,如果您不使用ILifecycle
,则可以跳过它。
如果您还有一些缓存集合要从没有会话的二级缓存中检索,您需要对集合类型工厂执行类似的工作,可以使用collectiontype.factory_class
进行配置,提供您自己的{{1产生覆盖Initialize
的集合类型,同样重复the loading from second level cache。
祝你好运,如果你试试这个。