【发布时间】:2021-06-17 07:45:01
【问题描述】:
当我执行 select IN SUBQUERY 时,我得到了我认为不必要的 CROSS JOIN,这会损害性能。如果有什么不同,我会使用 Postgres。
我的目标是生成以下查询
select a1.first_name from author a1
where a1.last_name = ?
and (a1.id in
(select distinct b.author_id
from book b
where (b.published_on between ? and ?)
group by b.author_id
having count(b.author_id) >= 2))
但我明白了
select a1.first_name from author a1
where a1.last_name = ?
and (a1.id in
(select distinct b.author_id
from book b
cross join author a2 where b.author_id = a2.id -- <<< I don't want this cross join!
and (b.published_on between ? and ?)
group by b.author_id
having count(b.author_id) >= 2))
代码
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<String> cq = cb.createQuery(Author.class);
Root<Author> authorRoot = cq.from(Author.class);
Subquery<Long> countSubquery = cq.subquery(Long.class);
Root<Book> bookRoot = countSubquery.from(Book.class);
Expression<Long> count = cb.count(bookRoot.get(Book_.author));
countSubquery.select(bookRoot.get(Book_.AUTHOR))
.distinct(true)
.where(cb.between(bookRoot.get(Book_.publishedOn),
LocalDate.of(2021, MARCH, 1),
LocalDate.of(2021, MARCH, 31)))
.groupBy(bookRoot.get(Book_.author))
.having(cb.greaterThanOrEqualTo(count, 2L));
cq.where(
cb.equal(authorRoot.get(Author_.lastName), "Smith"),
cb.in(authorRoot.get(Author_.ID)).value(countSubquery));
cq.select(authorRoot.get(Author_.FIRST_NAME));
TypedQuery<String> query = entityManager.createQuery(cq);
return query.getResultList();
实际上,我是从用户驱动的查询生成器生成查询,这段代码重现了我遇到的确切问题。
当使用查询生成器时,用户最终可能会在子查询中进行多项选择,因此我需要它尽可能地执行。
我不明白为什么我需要任何联接/交叉联接才能使我的查询正常工作。
实体
@Entity
public class Author {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Book> books;
}
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
private LocalDate publishedOn;
}
【问题讨论】:
-
Criteria API 仅生成一个 JPQL 查询,我在该部分中看不到任何条件逻辑,这意味着您可以跳过它并直接使用 JPQL 查询。如果您必须生成各种
where、having或order by条件,那么使用它是有意义的。就像现在一样,您不需要它。 -
TypedQuery<Author>会给你带来麻烦。返回的类型是String,而不是Author -
@coladict TypedQuery
确实可以工作/编译,并成功返回字符串!我认为这是由于运行时类型擦除 -
代码更新为 CriteriaQuery
和 TypedQuery
标签: java sql jpa criteria criteria-api