Hibernate 在访问关联实体的 id 时生成 SQL 查询
Posted
技术标签:
【中文标题】Hibernate 在访问关联实体的 id 时生成 SQL 查询【英文标题】:Hibernate generating SQL queries when accessing associated entity's id 【发布时间】: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。
如何调查问题?这种不正确行为的一些可能原因是什么?
【问题讨论】:
【参考方案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;
...
相关问题
Hibernate Annotations - Which is better, field or property access?参考文献
Proxy loaded on getId-call when using annotations on fields proxy getId => why sql is generated ! HHH-3718(如果这个问题可以解决的话)【讨论】:
天哪,谢谢,我从来没有想过这是个问题。 HHH-3718 终于修复了:hibernate.atlassian.net/browse/HHH-3718【参考方案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 起已更改。
【讨论】:
以上是关于Hibernate 在访问关联实体的 id 时生成 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate @OneToMany 关联尝试设置空 FK 值
MySQL 使用 JPA + Hibernate 的 9 个高性能技巧