我通过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的表模式:
表父列: ID,键入
表国家列: ID、代码、语言
表PARENT_COUNTRY列: PARENT_ID,COUNTRY_ID
表CHILD_A列: ID,描述
基于此,我试图创建一个CriteriaQuery,使用EntityGraph来控制动态关联实体的加载。下面的代码展示了给定查询的方式:
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=?关系所做的筛选。
在这个用例中,有人知道如何防止双连接吗?
发布于 2022-10-21 15:18:51
最后,在为这个问题奋斗了一整天之后,我找到了解决办法。结果是,解决方案是使用JPA中的一个名为fetch-join的特性,它允许您加入一个关系并使用它来获取将用于填充实体的数据。
然而,必须进行一些语法调整才能使其工作。另外,当我使用标准构建器获取-连接一个关系时,我也应该从EntityGraph中忽略它们,这样我就不会两次加入同一个实体。下面的片段显示了它是如何工作的:
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
https://stackoverflow.com/questions/74152293
复制相似问题