首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么使用EntityGraph的JPA继承会查询两次相关实体?

为什么使用EntityGraph的JPA继承会查询两次相关实体?
EN

Stack Overflow用户
提问于 2022-10-21 10:28:53
回答 1查看 37关注 0票数 1

我通过JPA映射了以下实体:

代码语言:javascript
复制
@Entity(name = "Parent")
@Table(name = "parent")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "type")
public abstract class Parent {

    @Id
    @GenericGenerator(name = "CustomUUIDGenerator", strategy = "package.CustomUUIDGenerator")
    @GeneratedValue(generator = "CustomUUIDGenerator", strategy = GenerationType.AUTO)
    private UUID id;

    @ManyToMany
    @Fetch(FetchMode.JOIN)
    @JoinTable(name = "parent_country", joinColumns = @JoinColumn(name = "parent_id"),
            inverseJoinColumns = @JoinColumn(name = "country_id"))
    private Set<Country> countries;

}

@Entity(name = "ChildA")
@Table(name = "child_a")
@DiscriminatorValue(value = "CHILD_A")
public class ChildA extends Parent {

    private String description;

}

这反映了以下Postgres的表模式:

表父列: ID,键入

表国家列: ID、代码、语言

表PARENT_COUNTRY列: PARENT_ID,COUNTRY_ID

表CHILD_A列: ID,描述

基于此,我试图创建一个CriteriaQuery,使用EntityGraph来控制动态关联实体的加载。下面的代码展示了给定查询的方式:

代码语言:javascript
复制
EntityGraph<ChildA> entityGraph = entityManager.createEntityGraph(ChildA.class);
entityGraph.addAttributeNodes(Parent_.COUNTRIES);

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<ChildA> criteriaQuery = criteriaBuilder.createQuery(ChildA.class);
Root<ChildA> root = criteriaQuery.from(ChildA.class);
List<Predicate> predicates = new ArrayList<>();
final Join<ChildA, Country> join = root.join(ChildA_.COUNTRIES, JoinType.LEFT);
predicates.add(criteriaBuilder.equal(join.get(Country_.CODE), "USA"));
criteriaQuery.where(predicates.toArray(Predicate[]::new));

TypedQuery<ChildA> finalQuery = entityManager
    .createQuery(criteriaQuery)
    .setHint(EntityGraphPersistence.LOAD_GRAPH, entityGraph);

List result = finalQuery.getResultList();

问题是,当我运行上面的查询时,控制台上打印了以下SQL:

代码语言:javascript
复制
select
    * -- All hibernate fields from all entities
from 
    child_a ca
inner join
    parent pa
        on ca.id=pa.id
left outer join
    parent_country pc
        on pc.parent_id = ca.id
left outer join
    country co
        on pc.country_id = co.id
left outer join
    parent_country pc_2
        on pc2.parent_id = ca.id
left outer join
    country co2
        on pc2.country_id = co2.id
where
(
    co.code=?
)

上面查询的问题是与PARENT_COUNTRY和COUNTRY的双重连接。在这个查询中,我根据国家代码(等于美国)过滤结果,所以我不想从其他国家(不同于美国)的数据库中得到结果。然而,由于第二个左联接到国家,父节点再次与国家相关,而忽略了对第一个co.code=?关系所做的筛选。

在这个用例中,有人知道如何防止双连接吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-10-21 15:18:51

最后,在为这个问题奋斗了一整天之后,我找到了解决办法。结果是,解决方案是使用JPA中的一个名为fetch-join的特性,它允许您加入一个关系并使用它来获取将用于填充实体的数据。

然而,必须进行一些语法调整才能使其工作。另外,当我使用标准构建器获取-连接一个关系时,我也应该从EntityGraph中忽略它们,这样我就不会两次加入同一个实体。下面的片段显示了它是如何工作的:

代码语言:javascript
复制
EntityGraph<ChildA> entityGraph = entityManager.createEntityGraph(ChildA.class);
//entityGraph.addAttributeNodes(Parent_.COUNTRIES);

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<ChildA> criteriaQuery = criteriaBuilder.createQuery(ChildA.class);
Root<ChildA> root = criteriaQuery.from(ChildA.class);
List<Predicate> predicates = new ArrayList<>();
//final Join<ChildA, Country> join = root.join(ChildA_.COUNTRIES, JoinType.LEFT);
Fetch<ChildA, Countries> fetch = root.fetch(ChildA_.COUNTRIES, JoinType.LEFT); // new line
Join<ChildA, Countries> join = (Join<ChildA, Countries>) fetch; // new line
predicates.add(criteriaBuilder.equal(join.get(Country_.CODE), "USA"));
criteriaQuery.where(predicates.toArray(Predicate[]::new));

TypedQuery<ChildA> finalQuery = entityManager
    .createQuery(criteriaQuery)
    .setHint(EntityGraphPersistence.LOAD_GRAPH, entityGraph);

List result = finalQuery.getResultList();

您可以在以下链接找到有关此主题的更多详细信息:https://discourse.hibernate.org/t/how-can-i-do-a-join-fetch-in-criteria-api/846/4

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74152293

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档