我有一个个人实体,有一个@ManyToOne自我关系名为“经理”。在大多数情况下,我不需要知道这个人的经理,所以我已经标记了与Fetch.LAZY的关系。但是,在每次我检索带有Person字段的实体时,JPA (使用Hibernate提供程序)都会递归地加载引用的Person...and (person的manager...and ),等等,直到加载整个管理链为止。所有这些查询都是在TypedQuery.getResultList()将控制权返回给我的代码之前执行的,所以这不是我无意中引用会话中某个地方的管理器并触发真正的惰性负载的问题。似乎JPA获取计划已经确定了由于某种原因整个管理链是必要的,并将它们全部加载。
我使用的是JPA持久性-api 2.2和hibernate-core 5.4.20
以下是Person类的缩写:
@Entity
@IdClass(BaseTenantKey.class)
public class Person {
@Id
@Column(name = "id", nullable = false, updatable = false, columnDefinition = ENTITY_ID_DEF)
private String id;
@Id
@Column(name = "tid", nullable = false, updatable = false, columnDefinition = TENANT_ID_DEF)
private String tid = "default";
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumnsOrFormulas(value = {
@JoinColumnOrFormula(formula = @JoinFormula(value = "tid", referencedColumnName = "tid")),
@JoinColumnOrFormula(column = @JoinColumn(name = "managerid", referencedColumnName = "id"))
})
@NotFound(action = NotFoundAction.IGNORE)
private Person manager;
...
}
@Embeddable
public class BaseTenantKey implements Serializable {
@Column(name = "id", nullable = false, updatable = false, columnDefinition = ENTITY_ID_DEF)
private String id;
@Column(name = "tid", nullable = false, updatable = false, columnDefinition = TENANT_ID_DEF)
private String tid;
}带有Person字段的示例实体是活动实体:
@Entity
public class Campaign {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "UUIDGenerator")
@Column(name = "id", nullable = false, updatable = false, columnDefinition = ENTITY_ID_DEF)
private String id;
@Column(name = "tid", columnDefinition = TENANT_ID_DEF)
private String tid = "default";
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumnsOrFormulas(value = {
@JoinColumnOrFormula(formula = @JoinFormula(value = "tid", referencedColumnName = "tid")),
@JoinColumnOrFormula(column = @JoinColumn(name = "created_by", referencedColumnName = "id",
nullable = false, updatable = false, columnDefinition = ENTITY_ID_DEF))
})
private Person createdBy;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumnsOrFormulas(value = {
@JoinColumnOrFormula(formula = @JoinFormula(value = "tid", referencedColumnName = "tid")),
@JoinColumnOrFormula(column = @JoinColumn(name = "owner", referencedColumnName = "id", nullable = false,
columnDefinition = ENTITY_ID_DEF, foreignKey = @ForeignKey(name = "fk_campaign_person_owner")))
})
private Person owner;
...
}在活动获取的情况下,我知道将引用createdBy和owner字段,因此我急切地使用实体图来获取它们:
final EntityGraph<Campaign> graph = getEntityManager().createEntityGraph(Campaign.class);
graph.addAttributeNodes(Campaign_.CREATED_BY, Campaign_.OWNER);
...
final TypedQuery<E> typedQuery = getEntityManager().createQuery(filterQuery);
typedQuery.setHint(GraphSemantic.FETCH.getJpaHintName(), graph);
typedQuery.setHint(QueryHints.HINT_READONLY, readOnly);
final List<E> result = typedQuery.getResultList();我可以看到,这有一个预期的结果,即生成的SQL联接在我的created_by和owner字段中:
select
campaign0_.id as id1_0_0_,
person1_.id as id1_35_1_,
person1_.tid as tid2_35_1_,
person2_.id as id1_35_2_,
person2_.tid as tid2_35_2_,
campaign0_.created_at as created_2_0_0_,
campaign0_.modified_at as modified3_0_0_,
campaign0_.tid as tid4_0_0_,
campaign0_.created_by as created30_0_0_,
campaign0_.version as version5_0_0_,
...
campaign0_.tid as formula58_0_,
campaign0_.tid as formula59_0_,
person1_.created_at as created_3_35_1_,
person1_.modified_at as modified4_35_1_,
...
person1_.tid as formula44_1_,
person2_.created_at as created_3_35_2_,
person2_.modified_at as modified4_35_2_,
...
person2_.tid as formula44_2_
from
campaign campaign0_
left outer join
person person1_
on campaign0_.created_by=person1_.id
and campaign0_.tid=person1_.tid
left outer join
person person2_
on campaign0_.owner=person2_.id
and campaign0_.tid=person2_.tid limit ?现在,问题就出现在这里。如果我为org.hibernate.internal.TwoPhaseLoad打开调试,我会看到createdBy和owner字段都解析给Person对象,id=CF21FF65-3A8C-40AB-BFAC-A7FD197C9512,并且根据调试,它们所指向的Person对象都被急切地用实体图覆盖来获取(正如预期的那样)。然而,当Hibernate开始处理该对象时,它会找到一个管理器引用(id=01ACB688-D415-42C3-81E7-91EEBBF26535),并立即发出一个没有上面提到的解释文本的查询:
o.h.engine.internal.TwoPhaseLoad : Resolving attributes for [com.company.domain.Campaign#79670EF1-872B-49D7-ACAC-67DA188249CA]
o.h.engine.internal.TwoPhaseLoad : Processing attribute `tid` : value = default
o.h.engine.internal.TwoPhaseLoad : Attribute (`tid`) - enhanced for lazy-loading? - false
o.h.engine.internal.TwoPhaseLoad : Processing attribute `createdBy` : value = BaseTenantKey(id=CF21FF65-3A8C-40AB-BFAC-A7FD197C9512, tid=default)
o.h.engine.internal.TwoPhaseLoad : Attribute (`createdBy`) - enhanced for lazy-loading? - false
o.h.engine.internal.TwoPhaseLoad : Overriding eager fetching using fetch graph. EntityName: com.company.domain.Campaign, associationName: createdBy, eager fetching: true
o.h.engine.internal.TwoPhaseLoad : Processing attribute `version` : value = 197
o.h.engine.internal.TwoPhaseLoad : Attribute (`version`) - enhanced for lazy-loading? - false
o.h.engine.internal.TwoPhaseLoad : Processing attribute `owner` : value = BaseTenantKey(id=CF21FF65-3A8C-40AB-BFAC-A7FD197C9512, tid=default)
o.h.engine.internal.TwoPhaseLoad : Attribute (`owner`) - enhanced for lazy-loading? - false
o.h.engine.internal.TwoPhaseLoad : Overriding eager fetching using fetch graph. EntityName: com.company.domain.Campaign, associationName: owner, eager fetching: true
o.h.engine.internal.TwoPhaseLoad : Done materializing entity [com.company.domain.Campaign#79670EF1-872B-49D7-ACAC-67DA188249CA]
o.h.engine.internal.TwoPhaseLoad : Resolving attributes for [com.company.domain.Person#component[id,tid]{id=CF21FF65-3A8C-40AB-BFAC-A7FD197C9512, tid=default}]
o.h.engine.internal.TwoPhaseLoad : Processing attribute `manager` : value = BaseTenantKey(id=01ACB688-D415-42C3-81E7-91EEBBF26535, tid=default)
o.h.engine.internal.TwoPhaseLoad : Attribute (`manager`) - enhanced for lazy-loading? - false
org.hibernate.SQL :
select
person0_.id as id1_35_0_,
person0_.tid as tid2_35_0_,
...
person0_.tid as formula44_0_
from
person person0_
where
person0_.id=?
and person0_.tid=?
o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [01ACB688-D415-42C3-81E7-91EEBBF26535]
o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [default]以类似的方式,在返回TypedQuery.getResultList()之前,还将获取上面的三层管理(id=01ACB688-D415-42C3-81E7-91EEBBF26535)。
我的问题是:
为什么当一个被声明为Fetch.LAZY的实体没有包含在GraphSemantic.FETCH应用的实体图中时,它会被检索?
注:
如果我放弃EntityGraph,让事情按照JPA实体注释进行,那么除了在活动查询之后立即对Person (id=CF21FF65-3A8C-40AB-A7FD197C9512)进行额外的查询外,我得到的结果几乎是相同的。和上面的问题相似,两次竞选活动中提到的人都是懒惰的,那么为什么要把他们取出来呢?
发布于 2020-12-21 21:38:10
我是个笨蛋。答案就在原木上:
WARN 2170 --- org.hibernate.cfg.AnnotationBinder : HHH000491: The [manager] association in the [demo.Person] entity uses both @NotFound(action = NotFoundAction.IGNORE) and FetchType.LAZY. The NotFoundAction.IGNORE @ManyToOne and @OneToOne associations are always fetched eagerly.删除@NotFound注释解决了问题。
发布于 2020-12-03 10:03:00
这可能是id类支持或@JoinFormula使用的问题。尝试使用可嵌入的ids来代替:
@Entity
public class Person {
@EmbeddedId
private BaseTenantKey id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(value = "tid", insertable = false, updatable = false, referencedColumnName = "tid"),
@JoinColumn(name = "managerid", insertable = false, updatable = false, referencedColumnName = "id")
})
@NotFound(action = NotFoundAction.IGNORE)
private Person manager;
...
}
@Embeddable
public class BaseTenantKey implements Serializable {
@Column(name = "id", nullable = false, updatable = false, columnDefinition = ENTITY_ID_DEF)
private String id;
@Column(name = "tid", nullable = false, updatable = false, columnDefinition = TENANT_ID_DEF)
private String tid;
}
@Entity
public class Campaign {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "UUIDGenerator")
@Column(name = "id", nullable = false, updatable = false, columnDefinition = ENTITY_ID_DEF)
private String id;
@Column(name = "tid", columnDefinition = TENANT_ID_DEF)
private String tid = "default";
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumns({
@JoinColumn(value = "tid", insertable = false, updatable = false, referencedColumnName = "tid"),
@JoinColumn(name = "created_by", referencedColumnName = "id",
nullable = false, updatable = false, columnDefinition = ENTITY_ID_DEF)
})
private Person createdBy;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumns({
@JoinColumn(value = "tid", insertable = false, updatable = false, referencedColumnName = "tid")),
@JoinColumn(name = "owner", referencedColumnName = "id", nullable = false,
columnDefinition = ENTITY_ID_DEF, foreignKey = @ForeignKey(name = "fk_campaign_person_owner"))
})
private Person owner;
...
}https://stackoverflow.com/questions/65101022
复制相似问题