如何在 JPA Criteriabuilder Select 语句中执行子查询?
How can I do a subquery in a JPA Criteria Builder Select statment?
我正在尝试使用 CritierBuilder/CrtieriaQuery 对 select 来自 table A 的某些字段执行 select 语句,如果该记录存在于另一个中,则使用布尔标志table。
基本上,我有一个 "officers" 列表和一个用户列表。用户是使用该系统的人,并且能够 bookmark/save 一名官员。当用户查询官员时,我希望能够显示他们收藏了哪些官员。
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 数据库控制台中,它(非常)有效。 returns 如果该官员被用户 123456789 收藏为书签,则为真,否则为书签列为空。
但是我在运行将其放入 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 语句的子查询部分以及如何返回布尔值。
问题是 cb.isNotNull(subRoot.get("id"))
只有在 subquery
结果存在时才有效(仅 returns 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();
}
我正在尝试使用 CritierBuilder/CrtieriaQuery 对 select 来自 table A 的某些字段执行 select 语句,如果该记录存在于另一个中,则使用布尔标志table。
基本上,我有一个 "officers" 列表和一个用户列表。用户是使用该系统的人,并且能够 bookmark/save 一名官员。当用户查询官员时,我希望能够显示他们收藏了哪些官员。
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 数据库控制台中,它(非常)有效。 returns 如果该官员被用户 123456789 收藏为书签,则为真,否则为书签列为空。
但是我在运行将其放入 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 语句的子查询部分以及如何返回布尔值。
问题是 cb.isNotNull(subRoot.get("id"))
只有在 subquery
结果存在时才有效(仅 returns 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();
}