我有
@Entity
public class Entity0 implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@OneToOne
private Entity1 entity1;
...
}
@Entity
public class Entity1 implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@OneToMany
private List<Entity2> entity2s;
...
}
@Entity
public class Entity2 implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@ManyToOne
private Entity3 entity3;
...
}
@Entity
public class Entity3 implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@Basic
private String property0;
...
}
(其中...
代表无参数构造函数、具有所有参数的构造函数以及所有属性的 getter 和 setter)我有以下工作查询,它适用于 Hibernate 5.2.12.Final (JPA 2.1):
Entity3 entity3 = new Entity3(1l, "some property value");
TypedQuery<Long> query = entityManager.createQuery(
"SELECT COUNT(e0) FROM Entity0 e0 LEFT JOIN e0.entity1 e1 "
+ "WHERE e0.entity1 != null "
+ "AND NOT EXISTS (SELECT e2 FROM e1.entity2s e2 LEFT OUTER JOIN e2.entity3 e3 WHERE e3.id = :someId)",
Long.class)
.setParameter("someId", entity3.getId());
//join is necessary in order to be able to access
//e0.entity1.entity2s because JPA isn't capable of
//accessing a reference of a reference
long retValue = query.getSingleResult();
现在,我想将查询转换为通过 JPA Criteria API 执行,但我似乎不知道如何在子查询中执行连接,因为我只找到Root.join
了属性,并且Root
始终引用由表示的实体一类。这导致以下尝试:
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("richtercloud_jpa-subquery-attribute-join_jar_1.0-SNAPSHOTPU" //persistenceUnitName
);
EntityManager entityManager = entityManagerFactory.createEntityManager();
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<Entity0> entity0Root = cq.from(Entity0.class);
Join<Entity0, Entity1> entity0Join = entity0Root.join(Entity0_.entity1,
JoinType.LEFT);
Root<Entity2> entity2JoinRoot = cq.from(Entity2.class);
//can only specify a Class here while selecting from a join is
//possible outside criteria api as well
// Join<Entity2, Entity3> entity2Join = cq.join(Entity1_.entity2s)
// .get(Entity1_.entity2s)
// .join(Entity2_.entity3);
Subquery<Entity2> entity2Subquery = cq.subquery(Entity2.class);
Root<Entity2> entity2SubqueryRoot = entity2Subquery.from(Entity2.class);
entity2Subquery.where(cb.equal(entity2SubqueryRoot.get(Entity2_.entity3), entity3));
cq.where(cb.and(cb.notEqual(entity0Root.get(Entity0_.entity1), cb.nullLiteral(Entity1.class))),
cb.not(cb.exists(entity2Subquery)));
TypedQuery<Long> criteriaQuery = entityManager.createQuery(cq);
long retValue = criteriaQuery.getSingleResult();
(只需要修复子查询)。
根据诸如JPA/Metamodel: Strange (inconsistent ?) example in Sun Docs 之类的帖子(这解释了 Oracle 作者非常周到地在他们的教程中列出了不可能的示例,但这是另一个问题),有CriteriaQuery.join
,但事实并非如此AFAIK。
如果 JPA 2.2 或(如果已经有任何计划,则为 2.3)会解决问题,以防除了我之外还有其他人没有找到解决方案,我会很感兴趣。
由于这是我加深理解的练习(JPQL 查询已经有效),我正在寻找一个可引用的参考资料,为什么这是不可能的,因为如果是这种情况,条件 API 的限制。
如果您想进一步调查,可以在https://gitlab.com/krichter/jpa-subquery-attribute-join找到 SSCCE,这可以节省您创建实体类的时间。