Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transaction management in Spring Boot and Spring data jpa

Altough transaction management works Spring Data Repositories create their own transaction and suspend the active one.

I have following Spring application:

Application class:

@SpringBootApplication
@EnableTransactionManagement
public class SpringbootTxApplication {... }

Service class:

@Service
public class EntityService {
    ...
    public void addEntityWithoutTransaction(MyEntity myEntity) {
        log.debug("addEntityWithoutTransaction start");
        myEntityRepository.save(myEntity);
        log.debug("addEntityWithoutTransaction end");
    }

    @Transactional
    public void addEntityTransaction(MyEntity myEntity) {
        log.debug("addEntityTransaction start");
        myEntityRepository.save(myEntity);
        log.debug("addEntityTransaction end");
    }
}

While exeucting my EntityServiceTest which executes each method once and having spring transaction log in trace, I get following output:

... TRACE ... o.s.t.i.TransactionInterceptor           : Getting transaction for [de.miwoe.service.EntityService.addEntityTransaction]
... DEBUG ... de.miwoe.service.EntityService           : addEntityTransaction start
... TRACE ... o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
... TRACE ... o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
... DEBUG ... de.miwoe.service.EntityService           : addEntityTransaction end
... TRACE ... o.s.t.i.TransactionInterceptor           : Completing transaction for [de.miwoe.service.EntityService.addEntityTransaction]

And

... DEBUG ... de.miwoe.service.EntityService           : addEntityWithoutTransaction start
... TRACE ... o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
... TRACE ... o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
... DEBUG ... de.miwoe.service.EntityService           : addEntityWithoutTransaction end

Obviously according to the log, the @Transactional-Annotation is working in addEntityTransaction, but the repository still creates its own transaction.

Why? Official docs (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions) describe it should not begin a new one if it already exists.

(Sometimes, Convention over Configuration seems more like Irritation over Convention over Configuration....)

Am I missing something?

(Complete code is also available here: https://github.com/miwoe/springboot-tx)

like image 867
miwoe Avatar asked Jul 18 '17 20:07

miwoe


People also ask

What is transaction management in JPA?

March 31, 2019. The transaction management is one of the most important Java Persistence API aspects if you are using Hibernate or any other JPA provider you should know how valuable transaction management is. The JPA transaction determines when the new, modified or deleted entity is synchronised with a database.

What is @transactional in Spring Data JPA?

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 transaction management in Spring boot?

Spring Boot implicitly creates a proxy for the transaction annotated methods. So for such methods the proxy acts like a wrapper which takes care of creating a transaction at the beginning of the method call and committing the transaction after the method is executed.

How does Spring Boot maintain transaction management?

Instead, you now need to do two things: Make sure that your Spring Configuration is annotated with the @EnableTransactionManagement annotation (In Spring Boot this will be done automatically for you). Make sure you specify a transaction manager in your Spring Configuration (this you need to do anyway).


2 Answers

Your question (main point):

... TRACE ... o.s.t.i.TransactionInterceptor           : Getting transaction for [de.miwoe.service.EntityService.addEntityTransaction]
... DEBUG ... de.miwoe.service.EntityService           : addEntityTransaction start
... TRACE ... o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
... TRACE ... o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
... DEBUG ... de.miwoe.service.EntityService           : addEntityTransaction end
... TRACE ... o.s.t.i.TransactionInterceptor           : Completing transaction for [de.miwoe.service.EntityService.addEntityTransaction]

Obviously according to the log, the @Transactional-Annotation is working in addEntityTransaction, but the repository still creates its own transaction.

Answer: You work inside one physical transaction.

When start call service spring starts new transaction and set TransactionStatus.isNewTransaction = true , when you call dao method spring check that method also transactional and create the second transaction for dao , BUT set for second transaction TransactionStatus.isNewTransaction = false .If you set required_new for dao method/class only in this case it be marked as TransactionStatus.isNewTransaction = true. At commit time only first transaction (physical ) is commited. If you mark the second transaction it will be ignored at commit time, and the first transaction is committed.

AbstractPlatformTransactionManager

    if (status.isNewTransaction()) {
       if (status.isDebug()) {
            logger.debug("Initiating transaction commit");
        }
        doCommit(status);
    }

You can check in debug mode.

Main point : you work inside one transaction that might be marked as commit or rollback.In TRACE you see, details about, what spring transaction does, and for you,it doesn't matter how many logical transactions creates inside on physical transaction. You have guaranty, for a transaction with propagation level REQUIRED ,that if you call the transactional method from another transaction method only one physical transaction is created and one is committed or rollbacked.

PROPAGATION_REQUIRED

When the propagation setting is PROPAGATION_REQUIRED, a logical transaction scope is created for each method upon which the setting is applied. Each such logical transaction scope can determine rollback-only status individually, with an outer transaction scope being logically independent from the inner transaction scope. Of course, in case of standard PROPAGATION_REQUIRED behavior, all these scopes will be mapped to the same physical transaction. So a rollback-only marker set in the inner transaction scope does affect the outer transaction’s chance to actually commit (as you would expect it to).

like image 94
xyz Avatar answered Oct 11 '22 03:10

xyz


The repository methods are @Tranactional because you're leveraging the JpaRepository interface and allowing the framework to implement that for you.

It chooses SimpleJpaRepository by default which uses @Transactional. Take a look at the source and you'll see where it's being used.

like image 39
Kyle Anderson Avatar answered Oct 11 '22 03:10

Kyle Anderson