繁体   English   中英

JPA 2 标准多对多子选择

[英]JPA 2 Criteria ManyToMany Subselect

我有以下情况:

人.类:

@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<>();

    ...
}

类别.类:

@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;

    ...
}

在这种情况下,我需要使用 JPA2 Criteria 进行查询,以替换此 HQL 查询:

SELECT obj FROM Person AS obj
WHERE EXISTS (
    SELECT category.id FROM obj.categories AS category
    WHERE category.name = 'Staff'
)

我试图与类别进行连接,但它将结果乘以每个人的类别数量:

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();

此条件导致此 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 ?
            )
    )

生成的 SQL 执行与person_category的 CROSS JOIN 并从子选择中执行 LEFT JOIN。

使用 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=?
    )

如何在 Criteria 中进行此查询?

您需要correlate()外部查询与子查询。 尝试以下几行(没有真实模型很难测试,但应该足够接近):

    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();

应该导致:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM