java 标准API:选择表达式IF [NOT] NULL< value>

yrdbyhpb  于 2022-12-10  发布在  Java
关注(0)|答案(3)|浏览(131)

我尝试将以下简单查询转换为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

我尝试了两种方法:
1.使用criteriaBuilder.function("ISNULL", Boolean.class, ticketAction.get(TicketAction_.ticketId)):问题是,如果ISNULL类似于ISNOTNULL(对于MySQL/MariaDB数据库),则不存在否定
1.使用criteriaBuilder.isNotNull(ticketAction.get(TicketAction_.ticketId)):但这将返回Predicate,而select语句只接受Expression
扩展目标:与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_actionid以及其他信息。this.hasTicketAction = ticketActionId != null;不是很满意,但工作。

谢谢大家张贴您的答案!

vnjpjtjt

vnjpjtjt1#

一种工作方法是使用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));

将返回具有和不具有TicketActionTicket的计数。
我不知道实际的原因,但问题 * 似乎是 * 在cb.isNotNull(..)(我的)JPA实现中要么总是获取似乎不是null的内容要么根据检查null的方式,它创建了一些INNER JOIN,因此null值从结果集中被丢弃。
(我正在使用OpenJPA)

2lpgd968

2lpgd9682#

对于此查询,您需要有@OneToMany类型的集合属性。此外,我不得不使用Tuple而不是TicketTuple(在this答案中引用),因为在处理没有票证的ticket_actions时,它会给出IllegalArgumentException(参数类型不匹配),我不知道您的架构中是否有这种可能性。
EclipseLink JPA实现、Derby数据库。
在类Ticket中,必须具有以下属性:

@OneToMany(mappedBy = "ticket")
private Collection<TicketAction> ticketActions;

条件查询代码:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createQuery(Tuple.class);
Root<Ticket> from = cq.from(Ticket.class);
Join<Ticket, TicketAction> join = from.join("ticketActions", JoinType.LEFT);

Expression<Boolean> caseHasAction = cb.<Boolean>selectCase()
      .when(cb.isNotNull(join.get("id")), true)
      .otherwise(false);

cq.multiselect(
      caseHasAction.as(Boolean.class)
     ,from.get("id")
);
TypedQuery<Tuple> tq = em.createQuery(cq);

for (Tuple tt : tq.getResultList()) {
    System.out.println("Res: " + tt.get(0) + " " + (tt.get(1) == null ? "null" : tt.get(1).toString()));
}

产生的查询:

SELECT CASE WHEN (t0.ID IS NOT NULL) THEN 1 ELSE 0 END , t1.ID FROM TICKET t1 LEFT OUTER JOIN TICKET_ACTION t0 ON (t0.TICKET_ID = t1.ID)
w8ntj3qf

w8ntj3qf3#

您可以使用下面的示例在select表达式中生成 is not null

CriteriaQuery<Tuple> cq = entityManager.getCriteriaBuilder().createTupleQuery();
Root<Test> pet = cq.from(Test.class);
cq.multiselect(pet.get("id1").isNotNull(), pet.get("id2"));

我认为生成的查询将抛出一个查询语法异常,因为jpa/hib可能不支持在select表达式中包含 is not null。如果是这种情况,那么一个可能的解决方案是在where子句中放置 is not null。您可以在这里找到这种情况的示例https://docs.oracle.com/cd/E19798-01/821-1841/gjitv/index.html

相关问题