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 .