«1. Введение

Во многих приложениях, ориентированных на данные, могут возникнуть ситуации, когда нам нужно проверить, существует ли уже конкретный объект.

В этом уроке мы обсудим несколько способов добиться именно этого с помощью Spring Data и JPA.

2. Образец объекта

Чтобы подготовить почву для наших примеров, давайте создадим объект Car с двумя свойствами, моделью и мощностью:

@Entity
public class Car {

    @Id
    @GeneratedValue
    private int id;

    private Integer power;
    private String model;
    
    // getters, setters, ...
}

3. Поиск по идентификатору

Интерфейс JpaRepository предоставляет existsById, который проверяет, существует ли в базе данных объект с заданным идентификатором:

int searchId = 2; // ID of the Car
boolean exists = repository.existsById(searchId)

Предположим, что searchId — это идентификатор автомобиля, который мы создали во время настройки теста. Ради воспроизводимости тестов мы никогда не должны использовать жестко запрограммированное число (например, «2»), потому что свойство id автомобиля, скорее всего, генерируется автоматически и может меняться со временем. Запрос existsById — самый простой, но наименее гибкий способ проверки существования объекта.

4. Использование производного метода запроса

Мы также можем использовать функцию производного метода запроса Spring для формулировки нашего запроса. В нашем примере мы хотим проверить, существует ли автомобиль с данным названием модели, поэтому мы разработали следующий метод запроса:

boolean existsCarByModel(String model);

Важно отметить, что название метода не является произвольным — оно должно соблюдать определенные правила. Затем Spring сгенерирует прокси для репозитория, чтобы он мог получить SQL-запрос из имени метода. Современные IDE, такие как IntelliJ IDEA, обеспечивают завершение синтаксиса для этого.

Когда запросы становятся более сложными — например, за счет включения упорядочения, ограничения результатов и нескольких критериев запроса — эти имена методов могут стать довольно длинными, вплоть до неразборчивости. Кроме того, производные методы запросов могут показаться волшебными из-за их неявной и «конвенциональной» природы.

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

5. Поиск по примеру

Пример — это очень мощный способ проверки существования, потому что он использует ExampleMatchers для динамического построения запроса. Итак, всякий раз, когда нам требуется динамичность, это хороший способ сделать это. Всестороннее объяснение Spring ExampleMatchers и того, как их использовать, можно найти в нашей статье Spring Data Query.

5.1. Сопоставитель

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

ExampleMatcher modelMatcher = ExampleMatcher.matching()
  .withIgnorePaths("id") 
  .withMatcher("model", ignoreCase());

Обратите внимание, что мы должны явно игнорировать путь id, потому что id является первичным ключом, а они выбираются автоматически по умолчанию.

5.2. Зонд

Далее нам нужно определить так называемый «зонд», который является экземпляром класса, который мы хотим найти. В нем установлены все необходимые для поиска свойства. Затем мы подключаем его к нашему nameMatcher и выполняем запрос:

Car probe = new Car();
probe.setModel("bmw");
Example<Car> example = Example.of(probe, modelMatcher);
boolean exists = repository.exists(example);

С большой гибкостью приходит большая сложность, и каким бы мощным ни был API ExampleMatcher, его использование создаст довольно много строк дополнительного кода. Мы предлагаем использовать это в динамических запросах или если никакой другой метод не подходит.

6. Написание пользовательского запроса JPQL с семантикой Exists

Последний метод, который мы рассмотрим, использует JPQL (Java Persistence Query Language) для реализации пользовательского запроса с семантикой exists:

@Query("select case when count(c)> 0 then true else false end from Car c where lower(c.model) like lower(:model)")
boolean existsCarLikeCustomQuery(@Param("model") String model);

Идея заключается в выполнении запроса счетчика без учета регистра на основе свойства модели, оценке возвращаемого значения и сопоставлении результата с логическим значением Java. Опять же, большинство IDE довольно хорошо поддерживают операторы JPQL.

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

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

В этом руководстве мы увидели, как проверить, существует ли объект в базе данных, используя Spring Data и JPA. Не существует жесткого и быстрого правила, когда какой метод использовать, потому что это во многом зависит от конкретного варианта использования и личных предпочтений.

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

Полный пример исходного кода можно найти на GitHub.