【问题标题】:Hibernate generating SQL queries when accessing associated entity's idHibernate 在访问关联实体的 id 时生成 SQL 查询
【发布时间】:2011-04-13 19:16:20
【问题描述】:

我的 Hibernate 实体看起来像这样(getter 和 setter 被忽略了):

@Entity
public class EntityA {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private EntityB parent;
}

@Entity
public class EntityB extends SuperEntity {
    @OneToMany(mappedBy = "parent")
    @Fetch(FetchMode.SUBSELECT)
    @JoinColumn(name = "parent_id")
    private Set<EntityA> children;
}

@MappedSuperclass
public class SuperEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private long itemId;
}

当我查询 EntityA 时,它加载正常,父关联被 Hibernate 代理替换(因为它是 Lazy)。如果我想访问父母的 id,我执行以下调用:

EntityA entityA = queryForEntityA();
long parentId = entityA.getParent().getItemId();

据我了解,调用不应往返于数据库,因为 Id 存储在 EntityA 表中,并且代理应该只返回该值。但是,在我的情况下,这会生成一个获取 EntityB 的 SQL 语句,然后才返回 Id。

如何调查问题?这种不正确行为的一些可能原因是什么?

【问题讨论】:

    标签: java hibernate orm proxy lazy-loading


    【解决方案1】:

    据我了解,调用不应往返于数据库,因为 Id 存储在 EntityA 表中,并且代理应该只返回该值。

    使用属性访问类型。您遇到的行为是字段访问类型的“限制”。以下是 Emmanuel Bernard 的解释:

    这很不幸,但在意料之中。这是字段级访问的限制之一。 基本上我们无法知道 getId() 确实只是去访问 id 字段。所以我们需要加载整个对象以保证安全。

    所以把你的代码改成:

    @Entity
    public class EntityA {
        private EntityB parent;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "parent_id")
        public EntityB getParent() {
            return parent; 
        }
        ...
    }
    
    @MappedSuperclass
    public class SuperEntity {
        private long itemId;
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "id")
        public long getItemId() { 
            return itemId;
        }
        ...
    }
    

    相关问题

    参考文献

    【讨论】:

    【解决方案2】:

    你说的有道理 - 因为 EntityA 包含父 ID,所以它不会产生数据库命中。我只是不确定 getParent() 调用是否实际加载了 EntityB 对象,无论您是否只对 ID 感兴趣。如果您想保存 DB 命中,您可以尝试将子集合(和任何其他字段)标记为 Lazy。

    @Entity
    public class EntityB : SuperEntity {
        @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
        @Fetch(FetchMode.SUBSELECT)
        @JoinColumn(name = "parent_id")
        private Set<EntityA> children;
    }
    

    【讨论】:

    • OneToMany 默认是 LAZY。
    【解决方案3】:

    至于 Hibernate:
    This behavior 自 Hibernate 5.2.12 起已更改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-24
      • 1970-01-01
      • 1970-01-01
      • 2021-04-21
      • 1970-01-01
      • 1970-01-01
      • 2012-03-09
      • 1970-01-01
      相关资源
      最近更新 更多