79

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?

Artyom Emelyanenko
  • 1,323
  • 1
  • 11
  • 16
  • 22
    @Transactional annotation in Repository is bad practice, better to use it in your **Service**. Cause one business action (marked as transaction) may consist of multiple requests to DB. even by several DAO. More here https://stackoverflow.com/questions/1079114/where-does-the-transactional-annotation-belong – Dan Brandt Sep 11 '18 at 13:00
  • 1
    @DanBrandt unless you have custom repo implementation with custom method that has to execute multiple queries as one transaction (eq. count all records, select 10 ids, select records by id, return all this data in 1 object). – Bojan Vukasovic Jul 10 '19 at 14:10

4 Answers4

83

CAUTION!

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

Doing so triggers the query annotated to the method as an updating query instead of 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 right away
        
        // JOHN still exist since john with active being false was not 
        // flushed into the database when @Modifying kicks in
        // so imagine if after `deleteInActiveUsers` line you called a native 
        // query or started a new transaction, both cases john 
        // was not deleted so it can lead to faulty business logic 
    }

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 exists 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 the @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 (known as Transaction Propagation) into one transaction (default propagation (REQUIRED)) see this answer for more info https://stackoverflow.com/a/25710391/1460591

To connect things together if you have multiple isolated transactions (e.g not having a transactional annotation on the service) hence you would have multiple sessions following the way spring data works hence you have multiple persistence contexts (aka 1st level cache) that means you might delete/modify an entity in a persistence context even with using flushAutomatically the same deleted/modified entity might be fetched and cached in another transaction's persistence context already, That would cause wrong business decisions due to wrong or un-synced data.

Youans
  • 4,801
  • 1
  • 31
  • 57
50

This will trigger the query annotated to the method as updating query instead of a selecting one. As the EntityManager might contain outdated entities after the execution of the modifying query, we automatically clear it (see JavaDoc of EntityManager.clear() for details). This will effectively drop all non-flushed changes still pending in the EntityManager. If you don't wish the EntityManager to be cleared automatically you can set @Modifying annotation's clearAutomatically attribute to false;

for further detail you can follow this link:-

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

Anshul Sharma
  • 3,432
  • 1
  • 12
  • 17
  • In recent JPA version both flags clearAutomatically and flushAutomatically set to false by default so if you want to clear or flush then the flag has to be set to true. – justMe Mar 04 '19 at 14:12
  • 1
    @justMe So are you saying that in recent versions, if we include modifying annotation alone without any properties like flush or clear, then this annotation is useless and does nothing? – theprogrammer Jul 04 '21 at 00:39
  • 1
    @theprogrammer all I am saying is those flags set to false by default doc here https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/Modifying.html, in regards to what happens if you don't set them? see the answer from Younas below. or official doc https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.modifying-queries – justMe Jul 05 '21 at 06:56
12

Queries that require a @Modifying annotation include INSERT, UPDATE, DELETE, and DDL statements.

Adding @Modifying annotation indicates the query is not for a SELECT query.

Kusal Dissanayake
  • 714
  • 14
  • 32
0

When you use only @Query annotation,you should use select queries However you @Modifying annotation you can use insert,delete,update queries above the method.

Procrastinator
  • 2,526
  • 30
  • 27
  • 36