Перейти к основному содержимому

Spring Data JPA @Modifying Annotation

· 5 мин. чтения

Задача: Наибольшая подстрока без повторений

Для заданной строки s, найдите длину наибольшей подстроки без повторяющихся символов. Подстрока — это непрерывная непустая последовательность символов внутри строки...

ANDROMEDA 42

1. Введение

В этом кратком руководстве мы научимся создавать запросы на обновление с помощью аннотации Spring Data JPA @Query . Мы достигнем этого, используя аннотацию @Modifying .

Во-первых, чтобы освежить нашу память, мы можем прочитать , как делать запросы, используя Spring Data JPA . После этого мы углубимся в использование аннотаций @Query и @Modifying . Наконец, мы обсудим, как управлять состоянием нашего контекста персистентности при использовании модифицирующих запросов.

2. Запрос в Spring Data JPA

Во-первых, давайте вспомним три механизма, которые Spring Data JPA предоставляет для запроса данных в базе данных :

  • Методы запроса
  • `Аннотация @Query`
  • Реализация пользовательского репозитория

Давайте создадим класс User и соответствующий репозиторий Spring Data JPA, чтобы проиллюстрировать эти механизмы:

@Entity
@Table(name = "users", schema = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private LocalDate creationDate;
private LocalDate lastLoginDate;
private boolean active;
private String email;

}
public interface UserRepository extends JpaRepository<User, Integer> {}

Механизм методов запросов позволяет нам манипулировать данными, получая запросы от имен методов:

List<User> findAllByName(String name);
void deleteAllByCreationDateAfter(LocalDate date);

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

Что касается аннотации @Query , то она предоставляет нам возможность написать конкретный JPQL или SQL-запрос в аннотации @Query :

@Query("select u from User u where u.email like '%@gmail.com'")
List<User> findUsersWithGmailAddress();

В этом фрагменте кода мы видим запрос, извлекающий пользователей, имеющих адрес электронной почты @gmail.com .

Первый механизм позволяет нам извлекать или удалять данные. Что касается второго механизма, то он позволяет выполнять практически любой запрос. Однако для обновления запросов мы должны добавить аннотацию @Modifying . Это будет темой этого урока.

3. Использование аннотации @Modifying

Аннотация @Modifying используется для улучшения аннотации @Query , чтобы мы могли выполнять не только запросы SELECT , но также запросы INSERT , UPDATE , DELETE и даже DDL .

Теперь давайте немного поиграем с этой аннотацией.

Во-первых, давайте рассмотрим пример запроса @Modifying UPDATE:

@Modifying
@Query("update User u set u.active = false where u.lastLoginDate < :date")
void deactivateUsersNotLoggedInSince(@Param("date") LocalDate date);

Здесь мы деактивируем пользователей, которые не вошли в систему с указанной даты.

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

@Modifying
@Query("delete User u where u.active = false")
int deleteDeactivatedUsers();

Как видим, этот метод возвращает целое число. Это функция запросов Spring Data JPA @Modifying , которая предоставляет нам количество обновленных сущностей.

Следует отметить, что выполнение запроса на удаление с помощью @Query работает иначе, чем методы запроса, производные от имени, в Spring Data JPA . Последний сначала извлекает сущности из базы данных, а затем удаляет их одну за другой. Это означает, что для этих сущностей будет вызываться метод жизненного цикла @PreRemove . Однако в первом случае к базе данных выполняется один запрос.

Наконец, давайте добавим удаленный столбец в нашу таблицу USERS с помощью DDL - запроса:

@Modifying
@Query(value = "alter table USERS.USERS add column deleted int(1) not null default 0", nativeQuery = true)
void addDeletedColumn();

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

3.1. Результат НЕ использования аннотации @Modifying

Давайте посмотрим, что произойдет, если мы не поместим аннотацию @Modifying в запрос на удаление.

По этой причине нам нужно создать еще один метод:

@Query("delete User u where u.active = false")
int deleteDeactivatedUsersWithNoModifyingAnnotation();

Обратите внимание на отсутствующую аннотацию.

Когда мы выполняем вышеуказанный метод, мы получаем исключение InvalidDataAccessApiUsage :

org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.QueryExecutionRequestException: 
Not supported for DML operations [delete com.foreach.boot.domain.User u where u.active = false]
(...)

Сообщение об ошибке довольно ясно; запрос не поддерживается для операций DML .

4. Управление контекстом персистентности

Если наш модифицирующий запрос изменяет сущности, содержащиеся в контексте постоянства, то этот контекст устаревает. Один из способов справиться с этой ситуацией — очистить контекст персистентности . Делая это, мы гарантируем, что в следующий раз контекст сохраняемости будет извлекать сущности из базы данных.

Однако нам не нужно явно вызывать метод clear() для EntityManager . Мы можем просто использовать свойство clearAutomatically из аннотации @Modifying :

@Modifying(clearAutomatically = true)

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

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

@Modifying(flushAutomatically = true)

Теперь EntityManager очищается перед выполнением нашего запроса.

5. Вывод

На этом мы завершаем эту краткую статью об аннотации @Modifying . Мы узнали, как использовать эту аннотацию для выполнения запросов на обновление, таких как INSERT, UPDATE, DELETE и даже DDL . После этого мы обсудили, как управлять состоянием контекста персистентности с помощью свойств clearAutomatically и flushAutomatically .

Как обычно, полный код этой статьи доступен на GitHub .