API критериев JPA объединяет атрибуты вместо корня класса
Я имею
@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;
...
}
(где ...
обозначает беспараметрический конструктор, конструктор со всеми аргументами, а также метод получения и установки для всех свойств), для которого у меня есть следующий рабочий запрос, который отлично работает с 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();
Теперь я хочу преобразовать запрос, который будет выполнен через API критериев JPA, но я, похоже, не понимаю, как выполнить объединение в подзапросе, который включает атрибуты, как я только нахожу 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 (противоречиво?) В Sun Docs (который объясняет, что авторы Oracle были настолько внимательны, чтобы перечислить невозможные примеры в своих руководствах, но это другая проблема), есть CriteriaQuery.join
, но это просто не так, афаик.
Мне было бы интересно, если бы JPA 2.2 или (2.3, если у вас уже есть какие-то планы) решали бы проблемы, если бы кроме меня не было решения.
Поскольку это упражнение для меня, чтобы углубить мое понимание (запрос JPQL уже работает), я ищу цитируемую ссылку, почему это невозможно из-за ограничения в API критериев, если это так.
Если вы хотите продолжить расследование, SSCCE можно найти по адресу https://gitlab.com/krichter/jpa-subquery-attribute-join что сэкономит вам время на создание классов сущностей.