Я использую 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.