API критериев гибернации: фильтрация по подмножеству

#java #hibernate #criteria #criteria-api

#java #гибернация #критерии #критерии-api

Вопрос:

У меня есть класс, который содержит набор Bs.

Я хотел бы создать критерии гибернации, чтобы получить все A, для которых набор Bs является надмножеством некоторого заданного набора.

Чтобы привести пример:

допустим, у нас есть три объекта типа A

a1, который имеет набор Bs = [b1, b2, b3]
a2, с набором = [b3, b4, b5]
a3, с набором = [b3, b5]

предположим, я хочу получить все A таким образом, чтобы его набор содержал [b3, b5]. Тогда результатом были бы a2 и a3

Надеюсь, я ясно выразился. Заранее спасибо!
Руководство

Ответ №1:

Я решил это следующим образом. Было сложно понять, что это сработало, но это довольно просто, как только вы это увидите.

         B[] subset = new B[] {b3, b5};
        DetachedCriteria.forClass(A.class).createAlias("bs", "b");

        for (B b : subset) {
            criteria.add(Restrictions.eq("b.id", b.getId()));
        }
  

Ответ №2:

Я считаю, что этот критерий сработал бы (при условии, что свойство на, A которое содержит набор B объектов, вызывается bs ):

 DetachedCriteria.forClass(A.class).add(Restrictions.and(
    Restrictions.in("bs", new B[] { b3, b5 }),
    Restrictions.notIn("bs", DetachedCriteria.forClass(B.class).add(
        Restrictions.notIn("this", new B[] { b3, b5 }))
    )
));
  

Хотя, возможно, не очень эффективно.

Комментарии:

1. это не работает. В примере также будет возвращен a1, потому что у a1 есть ‘b’, который находится в { b3, b5 }.

2. ага. извините, что пропустил эту деталь. смотрите правку, которую я только что сделал. я не уверен, работает ли «это» в запросах критериев (на данный момент ускользает из моей головы), но если нет, то вы можете использовать идентификатор объекта.

Ответ №3:

Прежде чем думать о критериях, вам нужно сначала записать необработанный SQL.

Вы могли бы сделать это в SQL следующим образом (предполагая, что существует таблица, AB объединяющая записи между A и B где fk_A указано на id in A и fk_B указывает на id in B ):

 SELECT fk_A, count(fk_B) FROM AB
   WHERE fk_B IN (b3, b5)
   GROUP BY fk_A 
   HAVING count(fk_B) = 2
  

Таким образом, вы не получите «неполные» записи из инструкции A as HAVING count(fk_B) , которая их фильтрует. Излишне говорить, что вам придется заменить 2 на соответствующее число, если количество Bs отличается.

Далее идет сложная часть преобразования этого в критерии 🙂 Некоторый псевдо (читай: непроверенный) код:

 Criteria criteria = getSession().createCriteria(A.class);
criteria.add(Restrictions.in("fk_B", new String[] {b3, b5}));

ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.count("fk_B"));
projectionList.add(Projections.groupProperty("fk_A"));
criteria.setProjection(projectionList);
  

Как вы можете видеть, в этом критерии все еще не хватает HAVING бита. К сожалению, HAVING пока не поддерживается в Criterions API. Однако вы должны получить список результатов и подсчетов и игнорировать те, где количество меньше требуемого.