1

我有

@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,这可以节省您创建实体类的时间。

4

0 回答 0