Nhibernate:只读属性导致急切加载

Posted

技术标签:

【中文标题】Nhibernate:只读属性导致急切加载【英文标题】:Nhibernate: readonly property causes eager load 【发布时间】:2013-09-12 12:03:25 【问题描述】:

我们最近升级到了最新版本的 NHibernate (3.3.3.4001),我遇到了 NHibernate 2.1.2.4000 中不存在的问题。这让我相信这可能是新的内置字节码提供程序的问题。

考虑以下映射:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Foo.Core.Domain" assembly="Foo.Core" default-access="property">
  <class name="EntityA" table="EntityA" lazy="true">

    <id name="Id" column="EntityAId">
      <generator class="native" />
    </id>

    <many-to-one name="EntityB" column="EntityBId" class="EntityB" not-null="true" />
    <many-to-one name="EntityC" column="EntityCId" class="EntityC" not-null="true" access="readonly" insert="true" update="false" />


  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Foo.Core.Domain" assembly="Foo.Core" default-access="property">
  <class name="EntityB" table="EntityB" lazy="true">

    <id name="Id" column="EntityBId">
      <generator class="native" />
    </id>

    <many-to-one name="EntityC" column="EntityCId" class="EntityC" not-null="true"  />

  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Foo.Core.Domain" assembly="Foo.Core" default-access="property">
  <class name="EntityC" table="EntityC" lazy="true">

    <id name="Id" column="EntityCId">
      <generator class="native" />
    </id>


  </class>
</hibernate-mapping>

这是我对 EntityA 的类定义:

Public Class EntityA

    Public Overridable Property Id As Integer

    Public Overridable Property EntityB As EntityB

    Public Overridable ReadOnly Property EntityC As EntityC
        Get
           Return If(EntityB IsNot Nothing, EntityB.EntityC, Nothing)
        End Get
    End Property


End Class

当我为 EntityA 的实例调用 Session.Get 时存在问题 - 它会立即导致为其对应的 EntityB 发出选择:

Session.Get(Of EntityA)(id) ' Causes the EntityB that EntityA references to be loaded as well.

我的最佳猜测是,字节码提供程序导致在构建代理时评估我的只读“EntityC”属性,这会强制加载引用的 EntityB。

有什么方法可以避免在 NHibernate 3.3.3 中使用这种类型的模型来避免急切负载的发生?

【问题讨论】:

抱歉,如果是愚蠢的评论,但是根据 EntityA.EntityC 属性代码,我没有看到 EntityA.EntityC 在映射中的意义(不知道您可以在 VB 中定义显式只读属性.Net,而 AFAIK,你不能在 C# 中) 要清楚,在我的实际实现中,这背后有一点(这只是一个人为的例子来说明问题) - 我需要该列用于非规范化目的(报告)。 我知道您为了简单起见设置了这个清晰的示例。我只是对 EntityA.EntityC 的这两个具有不同语义的定义感到困惑,一个在映射中,一个在代码中,并且想知道代理类的预期行为。也许您可以对此有所了解,因为它似乎是您问题的核心。 【参考方案1】:

我在 NH 3.3.3 SP1 上对此类层次结构和映射进行了一些测试,这是我的观察结果:

使用 session.Get&lt;&gt; 加载 EntityA,然后对该对象执行某些操作 不触及属性 EntityB、EntityC、EntityB 将 不被加载。触摸任一属性将触发查询 当然,这是正常的。

加载 EntityA,更新与属性无关的内容 EntityB 和 EntityC 喜欢更改 EntityA 名称,然后回滚 交易,没有额外的 SELECT 发出。

加载 EntityA,什么都不做,然后提交事务,第二个 SELECT 为EntityB发行。

加载EntityA,对session做一些查询,然后说SELECT EntityB 已发布。

所有测试都在 FlushMode.Auto 下完成。

从这些,我得出结论,NHibernate在这种情况下的行为是完全可以预料的:在进行flush时,NH需要检查对象的脏污程度,它需要获取属性EntityC的值与之前的值进行比较,这就是触发有问题的 SELECT 的原因。

这绝对不是因为session.Get&lt;&gt; 或新代理。您可以轻松地进行更多测试来证明这一点。不过,我不明白为什么 NH 2.1.2 会有所不同。

我也试过在 NH 3.3.1 上测试,结果和 NH 3.3.3 SP1 完全一样。

【讨论】:

感谢您的输入 - 我的测试案例属于您上面提到的“加载 EntityA,什么都不做,然后提交事务...”案例。现在,您的结论是有道理的 - 所以让我们考虑一下我上面的更新映射,其中我有 EntityA -> EntityC 关系映射为: insert="true" update="false" - 这不会消除刷新所需的脏检查吗?跨度> 刚刚测试过。 NH Profiler 显示仍然发出第二个 SELECT。看来您将需要在类和属性中进行不同的安排,或者覆盖 NHibernate 的脏检查以避免该 SELECT。

以上是关于Nhibernate:只读属性导致急切加载的主要内容,如果未能解决你的问题,请参考以下文章

Nhibernate - 分页和急切加载实体

nHibernate 不加载第三级属性(不可刷新缓存)

在 NHibernate 3.0 Linq 中急切加载多个兄弟姐妹和孙辈(堂兄弟?)的良好行为

插入时 NHibernate 组件非空属性

NHibernate 急切负载

在 NHibernate 中强制进行急切的选择