سؤال

أنا أستخدم JPA 2.0 في Eclipselink 2.3.2 والتي لدي علاقة كثيرة بين المنتجات وألوانها. يمكن أن يحتوي المنتج على العديد من الألوان ويمكن أن يرتبط اللون بالعديد من المنتجات. يتم التعبير عن هذه العلاقة في قاعدة البيانات بواسطة ثلاثة جداول.

  • منتج
  • 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؟


تعديل:

استعلام هذا المعايير:

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   

تعديل:

الاستعلام عن المعايير التالية مع 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 IN() بند بدلا من NOT EXISTS() ولتخلى عن هذا الانضمام الزائد؟

تم بالفعل الإبلاغ عن الانضمام الزائد الذي أنتجته هذا الاستعلام باعتباره أ حشرة.

هل كانت مفيدة؟

المحلول

ما يلي هو الاستعلام عن المعايير بخصوص 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

هذا الاستعلام يعيد مجموعة النتائج المطلوبة. ومع ذلك ، فإنه ينتج صلة زائدة كما يمكن أن نرى ولكن يبدو أن هذا هو حشرة.


يحرر:

محاولة نفس الاستعلام على السبات ، تبدو طريقة كتابة هذا الاستعلام المعايير غير صحيحة. مجموعة من الانضمام والاستماع الفرعي يؤدي إلى إنتاج استعلام 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top