【问题标题】:Why JPA Inheritance with EntityGraph queries twice a related entity?为什么 JPA Inheritance with EntityGraph 会查询两次相关实体?
【发布时间】:2022-10-24 03:38:30
【问题描述】:

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

@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 的表模式:

表 PARENT 列:ID、TYPE

表 COUNTRY 列:ID、CODE、LANGUAGE

表 PARENT_COUNTRY 列:PARENT_ID、COUNTRY_ID

表 CHILD_A 列:ID、DESCRIPTION

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

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:

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=? 的第一个关系进行的过滤器被忽略。

有谁知道如何防止这个用例中的双重加入?

【问题讨论】:

    标签: java hibernate jpa hibernate-criteria entitygraph


    【解决方案1】:

    终于在这个问题上挣扎了一整天后,我找到了解决方案。 事实证明,解决方案是使用来自 JPA 的名为 fetch-join 的功能,它允许您加入关系并使用它来获取将用于填充实体的数据。

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-28
      • 1970-01-01
      • 2017-04-30
      相关资源
      最近更新 更多