«1. Обзор

API критериев JPA можно использовать для простого добавления нескольких условий И/ИЛИ при запросе записей в базе данных. В этом руководстве мы рассмотрим быстрый пример запросов критериев JPA, которые объединяют несколько предикатов AND/OR.

Если вы не знакомы с предикатами, рекомендуем сначала прочитать об основных критериальных запросах JPA.

2. Пример приложения

В наших примерах мы рассмотрим перечень сущностей Item, каждая из которых имеет идентификатор, имя, цвет и класс:

@Entity
public class Item {

    @Id
    private Long id;
    private String color;
    private String grade;
    private String name;
    
    // standard getters and setters
}

3. Объединение двух предикатов ИЛИ с использованием AND Predicate

Давайте рассмотрим случай использования, когда нам нужно найти элементы, имеющие оба:

  1. red or blue color
    AND
  2. A or B grade

Мы можем легко сделать это, используя составные предикаты and() и or() API JPA Criteria.

Для начала давайте настроим наш запрос:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Item> criteriaQuery = criteriaBuilder.createQuery(Item.class);
Root<Item> itemRoot = criteriaQuery.from(Item.class);

Теперь нам нужно построить предикат для поиска элементов, имеющих синий или красный цвет:

Predicate predicateForBlueColor
  = criteriaBuilder.equal(itemRoot.get("color"), "blue");
Predicate predicateForRedColor
  = criteriaBuilder.equal(itemRoot.get("color"), "red");
Predicate predicateForColor
  = criteriaBuilder.or(predicateForBlueColor, predicateForRedColor);

Далее, давайте создадим Предикат для поиска элементов класса A или B:

Predicate predicateForGradeA
  = criteriaBuilder.equal(itemRoot.get("grade"), "A");
Predicate predicateForGradeB
  = criteriaBuilder.equal(itemRoot.get("grade"), "B");
Predicate predicateForGrade
  = criteriaBuilder.or(predicateForGradeA, predicateForGradeB);

Наконец, мы определяем предикат И для этих двух, применяем метод where() и выполняем наш запрос:

Predicate finalPredicate
  = criteriaBuilder.and(predicateForColor, predicateForGrade);
criteriaQuery.where(finalPredicate);
List<Item> items = entityManager.createQuery(criteriaQuery).getResultList();

4. Объединение двух И Предикаты с использованием предиката ИЛИ

С другой стороны, давайте рассмотрим случай, когда нам нужно найти элементы, имеющие:

  1. red color and grade D
    OR
  2. blue color and grade B

Логика очень похожа, но здесь мы сначала создаем два предиката И, а затем объединяем их, используя ИЛИ Predicate:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Item> criteriaQuery = criteriaBuilder.createQuery(Item.class);
Root<Item> itemRoot = criteriaQuery.from(Item.class);

Predicate predicateForBlueColor
  = criteriaBuilder.equal(itemRoot.get("color"), "red");
Predicate predicateForGradeA
  = criteriaBuilder.equal(itemRoot.get("grade"), "D");
Predicate predicateForBlueColorAndGradeA
  = criteriaBuilder.and(predicateForBlueColor, predicateForGradeA);

Predicate predicateForRedColor
  = criteriaBuilder.equal(itemRoot.get("color"), "blue");
Predicate predicateForGradeB
  = criteriaBuilder.equal(itemRoot.get("grade"), "B");
Predicate predicateForRedColorAndGradeB
  = criteriaBuilder.and(predicateForRedColor, predicateForGradeB);

Predicate finalPredicate
  = criteriaBuilder
  .or(predicateForBlueColorAndGradeA, predicateForRedColorAndGradeB);
criteriaQuery.where(finalPredicate);
List<Item> items = entityManager.createQuery(criteriaQuery).getResultList();

5. Заключение

В этом руководстве мы использовали JPA Criteria API для реализации вариантов использования, в которых нам нужно было комбинировать предикаты AND/OR.

Как обычно, полный исходный код, используемый для этого руководства, находится на GitHub.