JPA criteria query in a many-to-many relationship

前端 未结 1 1448
我寻月下人不归
我寻月下人不归 2021-01-12 22:36

I\'m using JPA 2.0 in EclipseLink 2.3.2 in which I have a many-to-many relationship between products and their colours. A product can have many colours and a colour can be a

相关标签:
1条回答
  • 2021-01-12 23:00

    The following is the criteria query regarding NOT IN() (I however, prefer NOT EXISTS() over 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();
    

    This produces the following SQL query.

    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
    

    This query returns the desired result set. It however, produces a redundant join as can be seen but this seems to be a bug.


    Edit:

    Trying the same query on Hibernate, the way of writing this criteria query looks incorrect. A combination of join and subquery result in producing the correct SQL query.

    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();
    

    This produces the following SQL query which would in turn be delegated to 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
    
    0 讨论(0)
提交回复
热议问题