Почему мы должны использовать @Modifying аннотацию для запросов в Data Jpa

например, у меня есть метод в моем интерфейсе CRUD, который удаляет пользователя из базы данных:

public interface CrudUserRepository extends JpaRepository<User, Integer> {

    @Transactional
    @Modifying
    @Query("DELETE FROM User u WHERE u.id=:id")
    int delete(@Param("id") int id, @Param("userId") int userId);
}

Этот метод будет работать только с аннотацией @Modification. Но в чем здесь нуждается аннотация? Почему can not spring анализирует запрос и понимает, что это модифицирующий запрос?

Ответ 1

Это вызовет запрос, аннотированный к методу, как обновление запроса, а не его выбор. Поскольку EntityManager может содержать устаревшие объекты после выполнения модифицирующего запроса, мы автоматически очищаем его (подробности см. В JavaDoc of EntityManager.clear()). Это позволит эффективно удалить все незаполненные изменения, все еще ожидающие рассмотрения в EntityManager. Если вы не хотите, чтобы EntityManager был очищен автоматически, вы можете установить @Modifying аннотацию clearAutomatically атрибут false,

для более подробной информации вы можете перейти по этой ссылке: -

http://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/jpa.repositories.html

Ответ 2

CAUTION!

Использование @Modifying(clearAutomatically=true) приведет к удалению любых ожидающих обновлений для управляемых прав доступа в контексте постоянства. Весенние состояния следующие:

Это вызывает запрос, аннотированный методу как обновление запрос вместо выбора. Поскольку EntityManager может содержать устаревшие сущности после выполнения модифицирующего запроса, мы делаем не очищать автоматически (см. JavaDoc EntityManager.clear() для деталей), так как это эффективно отбрасывает все не очищенные изменения все еще в ожидании в EntityManager. Если вы хотите, чтобы EntityManager очищаться автоматически, вы можете установить аннотации @Modifying атрибут clearAutomatics для true.

К счастью, начиная с Spring Boot 2.0.4.RELEASE Spring Data добавлен флаг flushAutomatically (https://jira.spring.io/browse/DATAJPA-806) для автоматической очистки любых управляемых объектов в контексте постоянства перед выполнением ссылки на проверку модифицирующего запроса https://docs.spring.io/spring-data/jpa/docs/2.0.4.RELEASE/api/org/springframework/data/jpa/repository/Modifying.html#flushAutomatically

Поэтому самый безопасный способ использовать @Modifying:

@Modifying(clearAutomatically=true, flushAutomatically=true)

Что произойдет, если мы не используем эти два флага?

Рассмотрим следующий код:

repo {
   @Modifying
   @Query("delete User u where u.active=0")
   public void deleteInActiveUsers();

}

Сценарий 1, почему flushAutomatically

 service {
        User johnUser = userRepo.findById(1); // store in first level cache
        johnUser.setActive(false);
        repo.deleteInActiveUsers();// BAM it won't delete JOHN

        // JOHN still exist since john with active being false was not 
        // flushed into the database when @Modifying kicks in
    }

Сценарий 2, почему clearAutomatically Далее рассмотрим, что johnUser.active уже ложен

service {
       User johnUser = userRepo.findById(1); // store in first level cache
       repo.deleteInActiveUsers(); // john is deleted now 
       System.out.println(userRepo.findById(1).isPresent()) // TRUE!!!
       System.out.println(userRepo.count()) // 1 !!!
       // JOHN still exist since in this transaction persistence context
       // was not cleared upon @Modifying query execution, John will still 
       // be fetched from 1st level cache 
       // 'clearAutomatically' takes care of doing the 
       // clear part on the object being modified
}

Так что если - в той же транзакции - вы играете с измененными объектами до или после строки, в которой @Modifying использует clearAutomatically & flushAutomatically если нет, то вы можете пропустить, используя эти флаги

Ответ 3

Запросы, требующие аннотации @Modifying включают @Modifying INSERT, UPDATE, DELETE и DDL.

Добавление аннотации @Modifying указывает, что запрос не для запроса SELECT.