Criteria API:选择Expression IF [NOT] NULL <value>

时间:2017-12-15 10:29:41

标签: java jpa criteria-api

我试图将以下简单查询转换为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)):问题是ISNULLISNOTNULL(对于MySQL / MariaDB数据库)
  2. 没有否定
  3. 使用criteriaBuilder.isNotNull(ticketAction.get(TicketAction_.ticketId)):但这会返回Predicate,而select语句只接受Expression s
  4. 拉伸目标:与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 |
    +-----------+---------+
    

    结果:

    最后,我最终使用了一个construtor查询,该查询接收id ticket_actionthis.hasTicketAction = ticketActionId != null;。在构造函数中有关于状态的决定:class Human{ object someObject1; object someObject2; object someObject3; void modifyObject(){ //[...] do some stuff that may require time and can modify someObject1 2 and 3 } } class Children : Human{ Human Parent1; Human Parent2; } 不太满意,但工作。

    感谢任何人发布您的答案!

3 个答案:

答案 0 :(得分:0)

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

将返回Ticket的{​​{1}}的计数,而不是TicketAction

我不知道实际原因,但问题似乎cb.isNotNull(..)(我的)JPA实施 总是取一些东西这似乎不是null ,具体取决于检查null的方式,它会创建一些INNER JOIN,以便删除null值从结果集中删除。

(我正在使用OpenJPA)

答案 1 :(得分:0)

对于此查询,您需要具有类型@OneToMany的集合属性。 另外,我必须使用Tuple而不是TicketTuple(在this回答中引用),因为在没有票证的情况下处理ticket_actions时它会给出IllegalArgumentException(参数类型不匹配),我不知道你的架构中是否有这种可能性。

EclipseLink JPA实现,Derby db。

在课程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)

答案 2 :(得分:-1)

您可以使用以下示例在选择表达式中生成 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 / hibernate可能不支持在select表达式中使不为空。如果是这种情况,那么一种可能的解决方案是将不为空放在where子句中。您可以在此处找到针对该案例的示例https://docs.oracle.com/cd/E19798-01/821-1841/gjitv/index.html