[java] Spring @Transactional - isolation, propagation

Good question, although not a trivial one to answer.

Propagation

Defines how transactions relate to each other. Common options:

  • REQUIRED: Code will always run in a transaction. Creates a new transaction or reuses one if available.
  • REQUIRES_NEW: Code will always run in a new transaction. Suspends the current transaction if one exists.

Isolation

Defines the data contract between transactions.

  • ISOLATION_READ_UNCOMMITTED: Allows dirty reads.
  • ISOLATION_READ_COMMITTED: Does not allow dirty reads.
  • ISOLATION_REPEATABLE_READ: If a row is read twice in the same transaction, the result will always be the same.
  • ISOLATION_SERIALIZABLE: Performs all transactions in a sequence.

The different levels have different performance characteristics in a multi-threaded application. I think if you understand the dirty reads concept you will be able to select a good option.


Example of when a dirty read can occur:

  thread 1   thread 2      
      |         |
    write(x)    |
      |         |
      |        read(x)
      |         |
    rollback    |
      v         v 
           value (x) is now dirty (incorrect)

So a sane default (if such can be claimed) could be ISOLATION_READ_COMMITTED, which only lets you read values which have already been committed by other running transactions, in combination with a propagation level of REQUIRED. Then you can work from there if your application has other needs.


A practical example of where a new transaction will always be created when entering the provideService routine and completed when leaving:

public class FooService {
    private Repository repo1;
    private Repository repo2;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void provideService() {
        repo1.retrieveFoo();
        repo2.retrieveFoo();
    }
}

Had we instead used REQUIRED, the transaction would remain open if the transaction was already open when entering the routine. Note also that the result of a rollback could be different as several executions could take part in the same transaction.


We can easily verify the behaviour with a test and see how results differ with propagation levels:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {

    private @Autowired TransactionManager transactionManager;
    private @Autowired FooService fooService;

    @Test
    public void testProvideService() {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        fooService.provideService();
        transactionManager.rollback(status);
        // assert repository values are unchanged ... 
}

With a propagation level of

  • REQUIRES_NEW: we would expect fooService.provideService() was NOT rolled back since it created it's own sub-transaction.

  • REQUIRED: we would expect everything was rolled back and the backing store was unchanged.

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to spring

Are all Spring Framework Java Configuration injection examples buggy? Two Page Login with Spring Security 3.2.x Access blocked by CORS policy: Response to preflight request doesn't pass access control check Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified Spring Data JPA findOne() change to Optional how to use this? After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName The type WebMvcConfigurerAdapter is deprecated No converter found capable of converting from type to type

Examples related to transactional

Spring @Transactional - isolation, propagation Spring - @Transactional - What happens in background?

Examples related to isolation

Spring @Transactional - isolation, propagation

Examples related to propagation

Spring @Transactional - isolation, propagation