[英]JPA 2 Criteria ManyToMany Subselect
I have the following scenario:我有以下情况:
Person.class:人.类:
@Entity
@Table(name = "person")
@SequenceGenerator(name = "seq_person", sequenceName = "seq_person", allocationSize = 1, initialValue = 1)
public class Person implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_pessoa")
@Column(name = "id")
private int id;
@Column(name = "name", nullable = false)
private String name;
@ManyToMany(fetch = FetchType.LAZY, cascade = { javax.persistence.CascadeType.REFRESH, javax.persistence.CascadeType.MERGE })
@JoinTable(name = "person_category", joinColumns = { @JoinColumn(name = "person_id") }, inverseJoinColumns = { @JoinColumn(name = "category_id") })
protected List<Category> categories = new ArrayList<>();
...
}
Category.class:类别.类:
@Entity
@Table(name = "category")
@SequenceGenerator(name = "seq_category", sequenceName = "seq_category", allocationSize = 1, initialValue = 1)
public class Category implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_category")
@Column(name = "id")
private int id;
@Column(name = "name", nullable = false)
private String name;
...
}
With this scenario, I need to do a query using JPA2 Criteria, to substitute this HQL query:在这种情况下,我需要使用 JPA2 Criteria 进行查询,以替换此 HQL 查询:
SELECT obj FROM Person AS obj
WHERE EXISTS (
SELECT category.id FROM obj.categories AS category
WHERE category.name = 'Staff'
)
I've tried to make a join with categories, but it multiply the result by the quantity of categories of each person:我试图与类别进行连接,但它将结果乘以每个人的类别数量:
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> root = cq.from(Person);
cq.select(root);
Subquery<Category> subquery = cq.subquery(Category.class);
Root<Category> subqueryFrom = subquery.from(Category.class);
subquery.select(subquery.from(clazz).get("id"));
Predicate predicateJoin = cb.equal(root.join("categories", JoinType.LEFT), subqueryFrom);
Predicate predicateName = cb.like(subqueryFrom.get("name"), "%" + content + "%");
subquery.where(cb.and(predicateJoin, predicateName));
cb.where(cb.exists(subquery));
TypedQuery<Person> typedQuery = this.entityManager.createQuery(cq);
List<Person> resultList = typedQuery.getResultList();
This criteria result in this SQL:此条件导致此 SQL:
select
person0_.id as id1_50_,
person0_.name as name29_50_
from
person person0_
left outer join
person_category categories2_
on person0_.id=categories2_.person_id
left outer join
category categoryp3_
on categories2_.category_id=categoryp3_.id
where
exists (
select
categoryp5_.id
from
person_category categoryp4_ cross
join
person_category categoryp5_
where
categoryp3_.id=categoryp4_.id
and (
categoryp4_.name like ?
)
)
The generated SQL do a CROSS JOIN with person_category
and do the LEFT JOIN out of subselect.生成的 SQL 执行与
person_category
的 CROSS JOIN 并从子选择中执行 LEFT JOIN。
Using the HQL Hibernate produces the following SQL:使用 HQL Hibernate 生成以下 SQL:
select
person0_.id as id1_50_,
person0_.name as name29_50_
from
person person0_
where
exists (
select
categoryp2_.id
from
person_category categories1_,
category categoryp2_
where
person0_.id=categories1_.person_id
and categories1_.category_id=categoryp2_.id
and categoryp2_.name=?
)
How I can do this query in Criteria?如何在 Criteria 中进行此查询?
You need to correlate()
the outer query with the subquery.您需要
correlate()
外部查询与子查询。 Try something along the following lines (it is hard to test without having the real model, but should be close enough):尝试以下几行(没有真实模型很难测试,但应该足够接近):
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> root = cq.from(Person.class);
cq.select(root);
Subquery<Category> subquery = cq.subquery(Category.class);
Root<Person> subqueryRoot = subquery.correlate(root);
Join<Person,Category> personCategories = subqueryRoot.join("categories");
subquery
.select(personCategories) // check out comment for possible needed change
.where(cb.equal(personCategories.get("name"), "Staff"));
cq.where(cb.exists(subquery));
TypedQuery<Person> typedQuery = this.entityManager.createQuery(cq);
List<Person> resultList = typedQuery.getResultList();
This should result in:这应该导致:
SELECT obj FROM Person AS obj
WHERE EXISTS (
SELECT category FROM obj.categories AS category
WHERE category.name = 'Staff'
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.