如何在 JPA Criteriabuilder Select 语句中执行子查询?
Posted
技术标签:
【中文标题】如何在 JPA Criteriabuilder Select 语句中执行子查询?【英文标题】:How can I do a subquery in a JPA Criteria Builder Select statment? 【发布时间】:2020-02-04 14:06:18 【问题描述】:我正在尝试使用 CritierBuilder/CrtieriaQuery 执行选择语句以从表 A 中选择某些字段,然后如果该记录存在于另一个表中,则使用布尔标志。
基本上,我有一个“官员”列表和一个用户列表。用户是使用系统的人,并且能够为官员添加书签/保存。当用户查询官员时,我希望能够显示他们已添加书签的官员。
SELECT o.FIRST_NAME, o.LAST_NAME,
(select CAST(1 AS BIT) from OFFICER_BOOKMARK b where b.OFFICER_ID=o.OFFICER_ID AND USER_ID=123456789) as BOOKMARKED
from OFFICER o;
所以这个查询,我在我的 h2 数据库控制台中运行,它(相当)有效。如果该官员被用户 123456789 标记为书签,则返回 true,否则为书签列返回 null。
但我无法将其转换为 jpa 标准查询...
public List<OfficerDTO> getOfficersDto()
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<OfficerDTO> cq = cb.createQuery(OfficerDTO.class);
Root<OfficerEntity> root = cq.from(OfficerEntity.class);
Root<OfficerBookmarkEntity> subRoot = cq.from(OfficerBookmarkEntity.class);
Subquery<Boolean> subquery = cq.subquery(Boolean.class);
subRoot.alias("bookmarked");
subquery.select(cb.isNotNull(subRoot.get("id")));
subquery.where(cb.equal(subRoot.get("officer").get("officerId"), root.get("officerId")));
subquery.where(cb.equal(subRoot.get("user").get("userId"), "123456789"));
cq.multiselect(
cb.construct(
OfficerDTO.class,
root.get("firstName"),
root.get("lastName"),
subquery.getSelection().as(Boolean.class)
)
);
TypedQuery<OfficerDTO> q = em.createQuery(cq);
return q.getResultList();
我想我已经很接近了,但我无法弄清楚 select 语句的子查询部分以及如何返回一个布尔值。
【问题讨论】:
【参考方案1】:问题是cb.isNotNull(subRoot.get("id"))
仅在存在subquery
结果时才有效(仅返回true
)。否则你有null
。所以你必须在更高的水平上检查subquery
结果。
这应该可以工作
public List<OfficerDTO> getOfficersDto()
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<OfficerDTO> cq = cb.createQuery(OfficerDTO.class);
Root<OfficerEntity> root = cq.from(OfficerEntity.class);
Subquery<Long> subquery = cq.subquery(Long.class); // or Integer (depends on id class)
Root<OfficerBookmarkEntity> subRoot =
subquery.from(OfficerBookmarkEntity.class);
Predicate officerPredicate = cb.equal(
subRoot.get("officer").get("officerId"),
root.get("officerId")
);
Predicate userPredicate = cb.equal(
subRoot.get("user").get("userId"),
"123456789"
);
subquery.select(subRoot.get("id")) // select subRoot id
.where(officerPredicate, userPredicate); // if you execute `.where` twice
// it replaces the previously added restrictions
cq.multiselect(
cb.construct(
OfficerDTO.class,
root.get("firstName"),
root.get("lastName"),
subquery.getSelection().isNotNull() // check if subquery result is present
)
);
return em.createQuery(cq).getResultList();
【讨论】:
谢谢!那行得通!我还不得不更改为使用休眠而不是 eclipselink...你知道 eclipselink 是否支持 select 语句中的子选择吗?这个问题和文档有点像它,***.com/questions/36077435/… 我对 eclipselink 不是很有经验以上是关于如何在 JPA Criteriabuilder Select 语句中执行子查询?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 JPA Criteriabuilder Select 语句中执行子查询?
如何告诉 JPA CriteriaBuilder 按返回的唯一数据列排序?
JPA CriteriaBuilder - 如何使用“IN”比较运算符