我正在尝试将以下简单查询转换为 JPA 标准 API:
SELECT ticket_action.ticket_id IS NOT NULL, ticket.id
FROM ticket
LEFT OUTER JOIN ticket_action ON ticket.id = ticket_action.ticket_id
我尝试了两种方法:
- 使用
criteriaBuilder.function("ISNULL", Boolean.class, ticketAction.get(TicketAction_.ticketId))
:问题是如果ISNULL
像ISNOTNULL
(对于 MySQL/MariaDB 数据库) - 使用
criteriaBuilder.isNotNull(ticketAction.get(TicketAction_.ticketId))
:但这会返回一个Predicate
而 select 语句只接受Expression
s
拉伸(stretch)目标:与 GROUP BY ticket.ticket_id IS NOT NULL
相同
请求的表结构
+-----------+---------+
| ticket |
+-----------+---------+
| name | type |
+-----------+---------+
| id | integer |
| title | varchar |
+-----------+---------+
+-----------+---------+
| ticket_action |
+-----------+---------+
| name | type |
+-----------+---------+
| id | integer |
| ticket_id | integer |
| title | varchar |
+-----------+---------+
结果:
最后,我最终使用了一个构造器查询,它接收 ticket_action
的 id
等。在构造函数中有关于状态的决定:this.hasTicketAction = ticketActionId != null;
不是很满意,但工作。
感谢任何人发布您的答案!
最佳答案
一种可行的方法是使用 CriteriaBuilder.selectCase(..)
。
使用 DTO
就像(Object[]
做同样的事情,但这从一开始就是类型安全的)
@Getter
@AllArgsConstructor
public class TicketTuple {
private Boolean hasAction;
private Long ticketId;
}
可以通过以下查询得到结果
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<TicketTuple> cq = cb.createQuery(TicketTuple.class);
Root<Ticket> from = cq.from(Ticket.class);
Join<Ticket, TicketAction> join = from.join("ticketAction", JoinType.LEFT);
Expression<Boolean> caseHasAction = cb.<Boolean>selectCase()
.when(cb.isNotNull(join), true)
.otherwise(false);
cq.multiselect(
caseHasAction.as(Boolean.class)
,from.get("id")
);
关于分组,我不太明白你需要分组什么,但是举个例子
cq.multiselect(
caseHasAction.as(Boolean.class)
,cb.count(from.get("id"))
).groupBy(caseHasAction.as(Boolean.class));
将返回具有和不具有 TicketAction
的 Ticket
的计数。
我不知道真正的原因,但问题似乎是 cb.isNotNull(..)
(my) JPA
实现 either 总是获取似乎不是 null
或 的内容,具体取决于检查 null
的方式创建一些 INNER JOIN
以便从结果集中删除 null
值。
(我正在使用 OpenJPA)
关于java - 标准 API : Select Expression IF [NOT] NULL <value>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47830390/