JPA criteria query in a many-to-many relationship

前端 未结 1 1447
我寻月下人不归
我寻月下人不归 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();
    CriteriaQuerycriteriaQuery=criteriaBuilder.createQuery(Colour.class);
    Metamodel metamodel = entityManager.getMetamodel();
    EntityType entityType = metamodel.entity(Colour.class);
    Root root = criteriaQuery.from(entityType);
    criteriaQuery.select(root);
    
    Subquerysubquery=criteriaQuery.subquery(Long.class);
    Root 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 typedQuery = entityManager.createQuery(criteriaQuery);
    List 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();
    CriteriaQuerycriteriaQuery=criteriaBuilder.createQuery(Colour.class);
    Metamodel metamodel = entityManager.getMetamodel();
    EntityType entityType = metamodel.entity(Colour.class);
    Root root = criteriaQuery.from(entityType);
    criteriaQuery.select(root);
    
    Subquerysubquery=criteriaQuery.subquery(Long.class);
    Root subRoot = subquery.from(Colour.class);
    subquery.select(subRoot.get(Colour_.colourId));
    SetJoin join = subRoot.join(Colour_.productSet, JoinType.INNER);
    
    ParameterExpression 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 typedQuery = entityManager.createQuery(criteriaQuery);
    List 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)
提交回复
热议问题