Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why do we have to use @Modifying annotation for queries in Data Jpa

for example I have a method in my CRUD interface which deletes a user from the database:

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); } 

This method will work only with the annotation @Modifying. But what is the need for the annotation here? Why cant spring analyze the query and understand that it is a modifying query?

like image 648
Artyom Emelyanenko Avatar asked Apr 27 '17 18:04

Artyom Emelyanenko


People also ask

Why do we use @query annotation?

The @Query annotation takes precedence over named queries, which are annotated with @NamedQuery or defined in an orm. xml file. It's a good approach to place a query definition just above the method inside the repository rather than inside our domain model as named queries.

What is clearAutomatically?

clearAutomatically. Defines whether we should clear the underlying persistence context after executing the modifying query. boolean. flushAutomatically. Defines whether we should flush the underlying persistence context before executing the modifying query.

What is @transactional annotation in spring boot?

The @Transactional annotation is the metadata that specifies the semantics of the transactions on a method. We have two ways to rollback a transaction: declarative and programmatic. In the declarative approach, we annotate the methods with the @Transactional annotation.

What is the use of query annotation in spring boot?

The @Query annotation declares finder queries directly on repository methods. While similar @NamedQuery is used on domain classes, Spring Data JPA @Query annotation is used on Repository interface. This frees the domain classes from persistence specific information, which is a good thing.


1 Answers

CAUTION!

Using @Modifying(clearAutomatically=true) will drop any pending updates on the managed entitites in the persistence context spring states the following :

Doing so triggers the query annotated to the method as an updating query instead of a selecting one. As the EntityManager might contain outdated entities after the execution of the modifying query, we do not automatically clear it (see the JavaDoc of EntityManager.clear() for details), since this effectively drops all non-flushed changes still pending in the EntityManager. If you wish the EntityManager to be cleared automatically, you can set the @Modifying annotation’s clearAutomatically attribute to true.

Fortunately, starting from Spring Boot 2.0.4.RELEASE Spring Data added flushAutomatically flag (https://jira.spring.io/browse/DATAJPA-806) to auto flush any managed entities on the persistence context before executing the modifying query check reference https://docs.spring.io/spring-data/jpa/docs/2.0.4.RELEASE/api/org/springframework/data/jpa/repository/Modifying.html#flushAutomatically

So the safest way to use @Modifying is :

@Modifying(clearAutomatically=true, flushAutomatically=true) 

What happens if we don't use those two flags??

Consider the following code :

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

Scenario 1 why flushAutomatically

 service {         User johnUser = userRepo.findById(1); // store in first level cache         johnUser.setActive(false);         repo.save(johnUser);          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     } 

Scenario 2 why clearAutomatically In following consider johnUser.active is false already

service {        User johnUser = userRepo.findById(1); // store in first level cache        repo.deleteInActiveUsers(); // you think that 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        // John's object was not cleared upon @Modifying query execution,         // John's object will still be fetched from 1st level cache         // `clearAutomatically` takes care of doing the         // clear part on the objects being modified for current         // transaction persistence context } 

So if - in the same transaction - you are playing with modified objects before or after the line which does @Modifying, then use clearAutomatically & flushAutomatically if not then you can skip using these flags

BTW this is another reason why you should always put @Transactional annotation on service layer, so that you only can have one persistence context for all your managed entities in the same transaction. Since persistence context is bounded to hibernate session, you need to know that a session can contain couple of transactions see this answer for more info https://stackoverflow.com/a/5409180/1460591 The way spring data works is that it joins the transactions together (aka transaction isolation) into one transaction (default isolation (required)) see this answer for more info https://stackoverflow.com/a/25710391/1460591

To connect things together if you have multiple transactions (e.g not having a transactional annotation on the service) hence you would have multiple session following the way spring data works hence you have multiple persistence contexts that mean you might delete/modify an element in a persistence context even with using flushAutomatically the same deleted/modified element might got fetched and cached in another transaction's persistence context that would cause business wrong decisions due to wrong or un-synced data

like image 157
Youans Avatar answered Sep 20 '22 10:09

Youans