2013-11-07 3 views
5

Я использую JPA 2.0 в EclipseLink 2.3.2, в котором у меня есть соотношение «многие-ко-многим» между продуктами и их цветами. Продукт может иметь много цветов, а цвет может быть связан со многими продуктами. Это соотношение выражается в базе данных тремя таблицами.Запрос критериев JPA в отношениях «многие-ко-многим»

  • продукт
  • prod_colour (присоединиться к таблице)
  • цвет

В таблице prod_colour имеет две опорные колонны prod_id и colour_id от связанных родительских таблиц product и colour соответственно.

Как очевидно, класс сущности Product имеет набор цветов - java.util.Set<Colour>, который имеет название colourSet.

Класс сущности Colour имеет набор продуктов - java.util.Set<Product>, который имеет название productSet.

Мне нужно получить список цветов из colour таблицы на основе prodId поставляется, который делает не соответствовать цветам в prod_colour таблицы.


Соответствующий JPQL будет примерно следующим.

FROM Colour colour 
WHERE colour.colourId 
NOT IN(
SELECT colours.colourId 
    FROM Product product 
    INNER JOIN product.colourSet colours 
    WHERE product.prodId=:id) 
ORDER BY colour.colourId DESC 

Он генерирует следующую инструкцию SQL.

SELECT t0.colour_id, t0.colour_hex, t0.colour_name 
FROM projectdb.colour t0 
WHERE t0.colour_id 
NOT IN (
     SELECT DISTINCT t1.colour_id 
     FROM prod_colour t3, projectdb.product t2, projectdb.colour t1 
     WHERE ((t2.prod_id = ?) 
     AND ((t3.prod_id = t2.prod_id) 
     AND (t1.colour_id = t3.colour_id)))) 
ORDER BY t0.colour_id DESC 

Поскольку это, в свою очередь, было запросом времени выполнения, было бы предпочтительно иметь запрос критериев. У меня нет понимания для создания запроса критериев в этих сложных отношениях.

У меня есть следующий запрос, который до сих пор не имеет отношения к предыдущему JPQL.

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder(); 
CriteriaQuery<Colour>criteriaQuery=criteriaBuilder.createQuery(Colour.class); 
Metamodel metamodel = entityManager.getMetamodel(); 
EntityType<Colour> entityType = metamodel.entity(Colour.class); 
Root<Colour> root = criteriaQuery.from(entityType); 
SetJoin<Colour, Product> join = root.join(Colour_.productSet, JoinType.INNER); 
ParameterExpression<Long> parameterExpression=criteriaBuilder.parameter(Long.class); 
criteriaQuery.where(criteriaBuilder.equal(join.get(Product_.prodId), parameterExpression)); 

TypedQuery<Colour> typedQuery = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, prodId); 
List<Colour> list=typedQuery.getResultList(); 

Как написать запрос критериев, соответствующий заданному JPQL?


EDIT:

Этот критерий запроса:

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder(); 
CriteriaQuery<Tuple>criteriaQuery=criteriaBuilder.createQuery(Tuple.class); 
Metamodel metamodel = entityManager.getMetamodel(); 
EntityType<Colour> entityType = metamodel.entity(Colour.class); 
Root<Colour> root = criteriaQuery.from(entityType); 
criteriaQuery.multiselect(root.get(Colour_.colourId)); 
SetJoin<Colour, Product> join = root.join(Colour_.productSet, JoinType.INNER); 
ParameterExpression<Long> parameterExpression=criteriaBuilder.parameter(Long.class); 
criteriaQuery.where(criteriaBuilder.equal(join.get(Product_.prodId), parameterExpression)); 

TypedQuery<Tuple> typedQuery = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, prodId); 
List<Tuple> list = typedQuery.getResultList(); 

в свою очередь, производит следующий SQL запрос.

SELECT t0.colour_id 
FROM projectdb.colour t0, prod_colour t2, projectdb.product t1 
WHERE ((t1.prod_id = 1) 
AND ((t2.colour_id = t0.colour_id) 
AND (t1.prod_id = t2.prod_id)))) 

Как соотнести этот запрос с подзапросом, чтобы он мог производить следующий SQL-запрос?

SELECT t0.colour_id, t0.colour_hex, t0.colour_name 
FROM projectdb.colour t0 
WHERE t0.colour_id 
NOT IN (
     SELECT t0.colour_id 
     FROM projectdb.colour t0, prod_colour t2, projectdb.product t1 
     WHERE ((t1.prod_id = 1) 
     AND ((t2.colour_id = t0.colour_id) 
     AND (t1.prod_id = t2.prod_id)))) 
ORDER BY t0.colour_id DESC 

EDIT:

Следующие критерии запроса наряду с NOT EXISTS() работ.

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder(); 
CriteriaQuery<Colour>criteriaQuery=criteriaBuilder.createQuery(Colour.class); 
Metamodel metamodel = entityManager.getMetamodel(); 
EntityType<Colour> entityType = metamodel.entity(Colour.class); 
Root<Colour> root = criteriaQuery.from(entityType); 
criteriaQuery.select(root); 
Subquery<Long>subquery=criteriaQuery.subquery(Long.class); 
Root<Product> subRoot = subquery.from(Product.class); 
subquery.select(root.get(Colour_.colourId)); 
Predicate paramPredicate = criteriaBuilder.equal(subRoot.get(Product_.prodId), prodId); 
Predicate correlatePredicate = criteriaBuilder.equal(root.get(Colour_.productSet), subRoot); 
subquery.where(criteriaBuilder.and(paramPredicate, correlatePredicate)); 
criteriaQuery.where(criteriaBuilder.exists(subquery).not()); 
criteriaQuery.orderBy(criteriaBuilder.desc(root.get(Colour_.colourId))); 

TypedQuery<Colour> typedQuery = entityManager.createQuery(criteriaQuery); 
List<Colour>list= typedQuery.getResultList(); 

Это, однако, производит запрос SQL с ненужным/дополнительным/избыточным присоединиться как следующий (возвращает нужный набор результатов, хотя, как это кажется).

SELECT t0.colour_id, t0.colour_hex, t0.colour_name 
FROM projectdb.colour t0 
WHERE 
NOT (EXISTS (
     SELECT t0.colour_id 
     FROM prod_colour t3, projectdb.product t2, projectdb.product t1 
     WHERE (((t1.prod_id = 1) 
     AND (t1.prod_id = t2.prod_id)) 
     AND ((t3.colour_id = t0.colour_id) 
     AND (t2.prod_id = t3.prod_id))))) 
ORDER BY t0.colour_id DESC 

Это должно быть просто, как,

SELECT t0.colour_id, t0.colour_hex, t0.colour_name 
FROM projectdb.colour t0 
WHERE 
NOT (EXISTS (
     SELECT t0.colour_id 
     FROM prod_colour t3, projectdb.product t2 
     WHERE (((t2.prod_id = 1)) 
     AND ((t3.colour_id = t0.colour_id) 
     AND (t2.prod_id = t3.prod_id))))) 
ORDER BY t0.colour_id DESC 

Есть ли способ иметь подзапрос с пунктом вместо NOT EXISTS()NOT IN() и избавиться от этого избыточно присоединиться?

Резервированное соединение, созданное по этому запросу, уже сообщалось как bug.

ответ

2

Критерии запроса относительно NOT IN() (я, однако, предпочитаю NOT EXISTS() более NOT IN()).

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder(); 
CriteriaQuery<Colour>criteriaQuery=criteriaBuilder.createQuery(Colour.class); 
Metamodel metamodel = entityManager.getMetamodel(); 
EntityType<Colour> entityType = metamodel.entity(Colour.class); 
Root<Colour> root = criteriaQuery.from(entityType); 
criteriaQuery.select(root); 

Subquery<Long>subquery=criteriaQuery.subquery(Long.class); 
Root<Product> subRoot = subquery.from(Product.class); 
subquery.select(root.get(Colour_.colourId)); 

Predicate paramPredicate = criteriaBuilder.equal(subRoot.get(Product_.prodId), prodId); 
Predicate correlatePredicate = criteriaBuilder.equal(root.get(Colour_.productSet), subRoot); 

subquery.where(criteriaBuilder.and(paramPredicate, correlatePredicate)); 
criteriaQuery.where(criteriaBuilder.in(root.get(Colour_.colourId)).value(subquery).not()); 
criteriaQuery.orderBy(criteriaBuilder.desc(root.get(Colour_.colourId))); 

TypedQuery<Colour> typedQuery = entityManager.createQuery(criteriaQuery); 
List<Colour> list=typedQuery.getResultList(); 

Производится следующий SQL-запрос.

SELECT t0.colour_id, t0.colour_hex, t0.colour_name 
FROM projectdb.colour t0 
WHERE NOT 
(t0.colour_id IN (
       SELECT t0.colour_id 
       FROM prod_colour t3, projectdb.product t2, projectdb.product t1 
       WHERE (((t1.prod_id = ?) 
       AND (t1.prod_id = t2.prod_id)) 
       AND ((t3.colour_id = t0.colour_id) 
       AND (t2.prod_id = t3.prod_id))))) 
ORDER BY t0.colour_id DESC 

Этот запрос возвращает желаемый набор результатов. Однако он создает избыточное соединение, как видно, но это похоже на bug.


Edit:

the same query Попытка на Hibernate, способ написания этого критерии запроса выглядит некорректно. Комбинация соединения и подзапроса приводит к созданию правильного SQL-запроса.

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder(); 
CriteriaQuery<Colour>criteriaQuery=criteriaBuilder.createQuery(Colour.class); 
Metamodel metamodel = entityManager.getMetamodel(); 
EntityType<Colour> entityType = metamodel.entity(Colour.class); 
Root<Colour> root = criteriaQuery.from(entityType); 
criteriaQuery.select(root); 

Subquery<Long>subquery=criteriaQuery.subquery(Long.class); 
Root<Colour> subRoot = subquery.from(Colour.class); 
subquery.select(subRoot.get(Colour_.colourId)); 
SetJoin<Colour, Product> join = subRoot.join(Colour_.productSet, JoinType.INNER); 

ParameterExpression<Long> parameterExpression=criteriaBuilder.parameter(Long.class); 
criteriaQuery.where(criteriaBuilder.in(root.get(Colour_.colourId)).value(subquery).not()); 
subquery.where(criteriaBuilder.equal(join.get(Product_.prodId), parameterExpression)); 
criteriaQuery.orderBy(criteriaBuilder.desc(root.get(Colour_.colourId))); 

TypedQuery<Colour> typedQuery = entityManager.createQuery(criteriaQuery); 
List<Colour> list = typedQuery.setParameter(parameterExpression, 1L).getResultList(); 

Это производит следующий SQL-запрос, который, в свою очередь, будет делегирован MySQL.

SELECT t0.colour_id, t0.colour_name, t0.colour_hex 
FROM projectdb.colour t0 
WHERE NOT (t0.colour_id IN 
      (SELECT t1.colour_id 
      FROM prod_colour t3, projectdb.product t2, projectdb.colour t1 
      WHERE ((t2.prod_id = ?) 
      AND ((t3.colour_id = t1.colour_id) 
      AND (t2.prod_id = t3.prod_id))))) 
ORDER BY t0.colour_id DESC 
Смежные вопросы