我试图将以下简单查询转换为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 拉伸目标:与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_action
等this.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;
}
不太满意,但工作。
感谢任何人发布您的答案!
答案 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