【问题标题】:How to lazy load a one-to-one composition via hql如何通过 hql 延迟加载一对一的组合
【发布时间】:2010-11-01 04:36:49
【问题描述】:

如果实体 A 与实体 B 具有双向一对一或零对一映射。

映射如下:

<class name="EntityA" table="TABLE_A" mutable="true" lazy="true">
    <id name="idA" type="long" column="pk_a" unsaved-value="null">
        <generator class="sequence">
            <param name="sequence">pk_a_seq</param>
        </generator>
    </id>
    <one-to-one name="propertyB" class="EntityB" property-ref="propertyA" constrained="true" outer-join="false"/>
</class>

和

<class name="EntityB" table="TABLE_B" mutable="true" lazy="true">
    <id name="idB" type="long" column="pk_b" unsaved-value="null">
        <generator class="sequence">
            <param name="sequence">pk_b_seq</param>
        </generator>
    </id>
    <many-to-one name="propertyA" class="EntityA" not-null="true" unique="true" lazy="proxy" column="fk_a"/>
</class>

当我为 EntityA 执行 hql 查询(或者更确切地说是命名的 hql 查询)时,hibernate 会急切地使用单独的 select 语句加载 EntityA#propertyB。

我的问题是,如果我的 hql 返回 1000 个 EntityA(都有各自的 EntityB),hibernate 将执行 n+1 个查询(第一个查询是 EntityA 返回 1000 个结果,而 n 个查询将来自EntityA#propertyB 选择延迟加载)。

但是,我不需要那些 EntityA#propertyB,这就是为什么我想延迟加载它们(不让休眠使用单独的 sql 查询)。

这可能吗?如果是,我该怎么做?

谢谢, 弗朗茨

【问题讨论】:

  • 我不完全理解问题所在。如果您为一组 EntityA 执行 hql,则每当您调用代理方法 EntityA.getPropertyB() 时,将通过单独的查询加载相应的 EntityA#propertyB。这是正常的延迟加载行为。您到底希望行为是什么?
  • 问题是因为我的 query.list() 得到了 n+1。根据我的调查,有两个原因:a.) 当我执行 session.getNamedQuery("myQueryString")..list() 时,Hibernate 急切地返回所有匹配的 EntityA 及其 EntityA#getPropertyB()初始化。 b.) EntityA#getPropertyB() 通过单独的 select 语句进行初始化。因此,使用一个 query.list(),我已经遇到了 n+1 问题。如果我可以删除 (a.) 或 (b.),那么我将能够解决我的 n+1。最好,我可以删除 (a.)

标签: hibernate hql lazy-loading one-to-one many-to-one


【解决方案1】:

我已经解决了这个问题。

我所做的是创建将字段 EntityA#propertyB 转换为名称为 EntityA#propertyBs 的集合。但我保留了 EntityA#getPropertyB() 和 EntityA#setPropertyB(EntityB propertyB) 访问器方法。

这些访问器方法的方法体现在是这样的:

public EntityB getPropertyB() {
    return CollectionUtils.get(propertyBs, 0);
}

public void setPropertyBs(EntityB propertyB) {
    propertyBs= Collections.singleton(propertyB);
}

然后在我的映射中,我映射了集合 EntityA#propertyBs 并指定了对“字段”的访问权限。

<set name="scheduledAdInfos" lazy="true" fetch="subselect" access="field" cascade="none" inverse="true">
    <key column="pk_a"/>
    <one-to-many class="EntityB"/>
</set>

使用此设置,您现在可以创建从拥有的 POJO (EntityA) 到拥有的 POJO (EntityB) 的惰性映射,即使 TABLE_A 归 TABLE_B 所有。

【讨论】:

    【解决方案2】:

    简短回答:不,您不能这样做,至少在不更改数据库和映射的情况下不能这样做。您基本上必须反转一对一映射和外键关系才能以您想要的方式工作。


    更长的答案: Hibernate 可以延迟加载关联。这样做的方法是注入一个代理对象,该对象包含被引用对象的 ID。

    在您的情况下,映射使得外键列位于 TABLE_B 中,即您使用多对一映射的位置。所以如果你加载一个 B,hibernate 会在 fk_a 列中找到 FK 引用,并可以创建一个保存这个值的代理。访问代理时会加载相应的实体。

    如果选择了表 A 中的一条记录怎么办? Hibenate 将创建一个 A 对象,但为了能够填充属性 B,它必须查看 TABLE_B,找到 fk_a=a.id 的对应行。 Hibernate 没有其他方法可以在延迟加载时找出要加载的记录。

    实际上,这将是对 Hibernate 的改进,因为它应该也能够在延迟加载时对其他唯一键进行加载,但当前的实现不允许这样做,也许你可以提出一个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-20
      • 2017-11-11
      • 2012-05-21
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多