JPA条件以多对多关系查询

Tin*_*iny 5 jpa criteria eclipselink jpql criteria-api

我在EclipseLink 2.3.2中使用JPA 2.0,其中我在产品和颜色之间存在多对多的关系.产品可以具有多种颜色,并且颜色可以与许多产品相关联.这种关系在数据库中由三个表表示.

  • 产品
  • prod_colour(连接表)
  • 颜色

prod_colour表有两个参考列prod_id,并colour_id从相关的父表productcolour分别.

很明显,实体类Product有一组颜色 - java.util.Set<Colour>它们被命名colourSet.

实体类Colour有一组产品 - java.util.Set<Product>名为productSet.

我需要根据提供colour表格从表格中获取颜色列表,这与表格中的颜色匹配.prodIdprod_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
Run Code Online (Sandbox Code Playgroud)

它生成以下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
Run Code Online (Sandbox Code Playgroud)

由于这又是运行时查询,因此最好具有条件查询.我没有深入了解在这种复杂关系中制作标准查询.

到目前为止,我有以下查询,这与前面的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();
Run Code Online (Sandbox Code Playgroud)

如何编写与给定的JPQL相对应的条件查询?


编辑:

此条件查询:

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();
Run Code Online (Sandbox Code Playgroud)

反过来生成以下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))))
Run Code Online (Sandbox Code Playgroud)

如何将此查询与子查询关联,以便它可以生成以下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   
Run Code Online (Sandbox Code Playgroud)

编辑:

以下条件查询以及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();
Run Code Online (Sandbox Code Playgroud)

但是,它生成带有不必要/额外/冗余连接的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
Run Code Online (Sandbox Code Playgroud)

这应该就像,

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
Run Code Online (Sandbox Code Playgroud)

有没有办法让NOT IN()子句与子句而不是NOT EXISTS()和摆脱这种冗余连接?

此查询生成的冗余连接已报告为错误.

Tin*_*iny 3

以下是有关的条件查询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();
Run Code Online (Sandbox Code Playgroud)

这会产生以下 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<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();
Run Code Online (Sandbox Code Playgroud)

该查询返回所需的结果集。然而,正如可以看到的那样,它产生了冗余连接,但这似乎是一个错误


编辑:

在 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();
Run Code Online (Sandbox Code Playgroud)

这会生成以下 SQL 查询,该查询又将委托给 MySQL。

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
Run Code Online (Sandbox Code Playgroud)