Spring @Transactional Propagation
In the previous article, we have covered some basics of transactional management and the spring @Transactional
annotation. In this article, we're going to learn about the transaction Propagation
in Spring. Simply put, transaction propagation specifies if any service will or will not participate in transaction and how will it perform if the calling services already have or does not have a transaction created already. In Spring transaction management, it provides seven types of Transaction Propagation
. Let's understand all of them one by one with examples. The code for this article is available over on GitHub.
Set-Up #
Application.properties:
logging.level.ROOT=INFO
logging.level.org.springframework.orm.jpa=DEBUG
logging.level.org.springframework.transaction=DEBUG
logging.pattern.console=%msg%n
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=caizhenhua
spring.datasource.password=123456
REQUIRED #
This is the default propagation mode. It supports a current transaction and create a new one if none exists.
Scenario | Service A | Service B | A calls B |
---|---|---|---|
1 | REQUIRED | REQUIRED | B joins existing transaction of A |
2 | Non-Transactional | REQUIRED | B creates a new transaction |
3 | REQUIRED | Non-Transactional | Similar to Scenario 1 |
Example 1:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example1 implements CommandLineRunner {
@Autowired
ServiceA serviceA;
@Override
public void run(String... args) throws Exception {
serviceA.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ServiceA {
@Autowired
ServiceB serviceB;
@Transactional
public void callServiceB() {
System.out.println("ServiceA#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ServiceB {
@Transactional
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.required.ServiceA.callServiceB]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(698477669<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@746fd19b]
ServiceA#callServiceB
Found thread-bound EntityManager [SessionImpl(698477669<open>)] for JPA transaction
Participating in existing transaction
ServiceB#run
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(698477669<open>)]
Closing JPA EntityManager [SessionImpl(698477669<open>)] after transaction
Example 2:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example2 implements CommandLineRunner {
@Autowired
ServiceWithoutTx service;
@Override
public void run(String... args) throws Exception {
service.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ServiceWithoutTx {
@Autowired
ServiceB serviceB;
public void callServiceB() {
System.out.println("ServiceWithoutTx#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ServiceB {
@Transactional
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
ServiceWithoutTx#callServiceB
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.required.ServiceB.run]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(408583632<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@6aae82cc]
ServiceB#run
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(408583632<open>)]
Closing JPA EntityManager [SessionImpl(408583632<open>)] after transaction
Example 3:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example3 implements CommandLineRunner {
@Autowired
ServiceB serviceB;
@Override
public void run(String... args) throws Exception {
serviceB.callServiceWithoutTx();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ServiceB {
@Autowired
ServiceWithoutTx service;
@Transactional
public void callServiceWithoutTx() {
System.out.println("ServiceB#callServiceWithoutTx");
service.run();
}
}
Service B:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ServiceWithoutTx {
@Autowired
ServiceB serviceB;
public void run() {
System.out.println("ServiceWithoutTx#run");
}
}
Some Console Output:
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.required.ServiceB.callServiceWithoutTx]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(1685778749<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@7f64bd7]
ServiceB#callServiceWithoutTx
ServiceWithoutTx#run
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(1685778749<open>)]
Closing JPA EntityManager [SessionImpl(1685778749<open>)] after transaction
SUPPORTS #
According to the JavaDoc, propagation SUPPORTS
is defined as:
Support a current transaction, execute non-transactionally if none exists.
Scenario | Service A | Service B | A calls B |
---|---|---|---|
4 | REQUIRED | SUPPORTS | Same as Scenario 1 |
5 | Non-Transactional | SUPPORTS | No transaction created |
Example 4:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example4 implements CommandLineRunner {
@Autowired
ServiceA serviceA;
@Override
public void run(String... args) throws Exception {
serviceA.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceA_SUPPORTS")
public class ServiceA {
@Autowired
ServiceB serviceB;
@Transactional
public void callServiceB() {
System.out.println("ServiceA#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_SUPPORTS")
public class ServiceB {
@Transactional(propagation = Propagation.SUPPORTS)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.supports.ServiceA.callServiceB]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(864186602<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@4bfe83d]
ServiceA#callServiceB
Found thread-bound EntityManager [SessionImpl(864186602<open>)] for JPA transaction
Participating in existing transaction
ServiceB#run
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(864186602<open>)]
Closing JPA EntityManager [SessionImpl(864186602<open>)] after transaction
Example 5:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example5 implements CommandLineRunner {
@Autowired
ServiceWithoutTx service;
@Override
public void run(String... args) throws Exception {
service.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service(value = "ServiceWithoutTx_SUPPORTS")
public class ServiceWithoutTx {
@Autowired
ServiceB serviceB;
public void callServiceB() {
System.out.println("ServiceWithoutTx#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_SUPPORTS")
public class ServiceB {
@Transactional(propagation = Propagation.SUPPORTS)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
ServiceWithoutTx#callServiceB
ServiceB#run
MANDATORY #
It supports a current transaction and throw exception if none exist.
Scenario | Service A | Service B | A calls B |
---|---|---|---|
6 | REQUIRED | MANDATORY | Same as Scenario 1 |
7 | Non-Transactional | MANDATORY | Throw exception |
Example 6:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example6 implements CommandLineRunner {
@Autowired
ServiceA serviceA;
@Override
public void run(String... args) throws Exception {
serviceA.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceA_MANDATORY")
public class ServiceA {
@Autowired
ServiceB serviceB;
@Transactional
public void callServiceB() {
System.out.println("ServiceA#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_MANDATORY")
public class ServiceB {
@Transactional(propagation = Propagation.MANDATORY)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.mandatory.ServiceA.callServiceB]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(64271451<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@602c167e]
ServiceA#callServiceB
Found thread-bound EntityManager [SessionImpl(64271451<open>)] for JPA transaction
Participating in existing transaction
ServiceB#run
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(64271451<open>)]
Closing JPA EntityManager [SessionImpl(64271451<open>)] after transaction
Example 7:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example7 implements CommandLineRunner {
@Autowired
ServiceWithoutTx service;
@Override
public void run(String... args) throws Exception {
service.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service(value = "ServiceWithoutTx_MANDATORY")
public class ServiceWithoutTx {
@Autowired
ServiceB serviceB;
public void callServiceB() {
System.out.println("ServiceWithoutTx#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_MANDATORY")
public class ServiceB {
@Transactional(propagation = Propagation.MANDATORY)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
ServiceWithoutTx#callServiceB
Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
...
Caused by: org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
REQUIRES_NEW #
According to the JavaDoc, propagation REQUIRES_NEW
is defined as:
Create a new transaction, and suspend the current transaction if one exists.
Note: REQUIRES_NEW
does not work if the calling service and callee are from the same class. This is because the @Transactional
is implemented based on the Spring AOP. The proxy pattern only intercept external method calls.
Scenario | Service A | Service B | A calls B |
---|---|---|---|
8 | REQUIRED | REQUIRES_NEW | Create a new transaction and suspend the current transaction |
9 | Non-Transactional | REQUIRES_NEW | Same as Scenario 2 |
Example 8:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example8 implements CommandLineRunner {
@Autowired
ServiceA serviceA;
@Override
public void run(String... args) throws Exception {
serviceA.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceA_REQUIRES_NEW")
public class ServiceA {
@Autowired
ServiceB serviceB;
@Transactional
public void callServiceB() {
System.out.println("ServiceA#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_REQUIRES_NEW")
public class ServiceB {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.requires_new.ServiceA.callServiceB]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(1160199488<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@c89e263]
ServiceA#callServiceB
Found thread-bound EntityManager [SessionImpl(1160199488<open>)] for JPA transaction
Suspending current transaction, creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.requires_new.ServiceB.run]
Opened new EntityManager [SessionImpl(506594173<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@5059d398]
ServiceB#run
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(506594173<open>)]
Closing JPA EntityManager [SessionImpl(506594173<open>)] after transaction
Resuming suspended transaction after completion of inner transaction
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(1160199488<open>)]
Closing JPA EntityManager [SessionImpl(1160199488<open>)] after transaction
Example 9:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example9 implements CommandLineRunner {
@Autowired
ServiceWithoutTx service;
@Override
public void run(String... args) throws Exception {
service.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service(value = "ServiceWithoutTx_REQUIRES_NEW")
public class ServiceWithoutTx {
@Autowired
ServiceB serviceB;
public void callServiceB() {
System.out.println("ServiceWithoutTx#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_REQUIRES_NEW")
public class ServiceB {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
ServiceWithoutTx#callServiceB
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.requires_new.ServiceB.run]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(246846952<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@c1386b4]
ServiceB#run
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(246846952<open>)]
Closing JPA EntityManager [SessionImpl(246846952<open>)] after transaction
NOT_SUPPORTED #
It does not suuport a current transaction and suspend the current transaction if one exists.
Scenario | Service A | Service B | A calls B |
---|---|---|---|
10 | REQUIRED | NOT_SUPPORTED | Suspend the current transaction |
11 | Non-Transactional | NOT_SUPPORTED | Same as Scenario 5 |
Example 10:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example10 implements CommandLineRunner {
@Autowired
ServiceA serviceA;
@Override
public void run(String... args) throws Exception {
serviceA.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceA_NOT_SUPPORTED")
public class ServiceA {
@Autowired
ServiceB serviceB;
@Transactional
public void callServiceB() {
System.out.println("ServiceA#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_NOT_SUPPORTED")
public class ServiceB {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.not_supported.ServiceA.callServiceB]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(1205133962<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@76d828ff]
ServiceA#callServiceB
Found thread-bound EntityManager [SessionImpl(1205133962<open>)] for JPA transaction
Suspending current transaction
ServiceB#run
Resuming suspended transaction after completion of inner transaction
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(1205133962<open>)]
Closing JPA EntityManager [SessionImpl(1205133962<open>)] after transaction
Example 11:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example11 implements CommandLineRunner {
@Autowired
ServiceWithoutTx service;
@Override
public void run(String... args) throws Exception {
service.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service(value = "ServiceWithoutTx_NOT_SUPPORTED")
public class ServiceWithoutTx {
@Autowired
ServiceB serviceB;
public void callServiceB() {
System.out.println("ServiceWithoutTx#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_NOT_SUPPORTED")
public class ServiceB {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
ServiceWithoutTx#callServiceB
ServiceB#run
NEVER #
It does not support a current transaction and throw exception if a transaction exists.
Scenario | Service A | Service B | A calls B |
---|---|---|---|
12 | REQUIRED | NEVER | Throw an exception |
Example 12:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example12 implements CommandLineRunner {
@Autowired
ServiceA serviceA;
@Override
public void run(String... args) throws Exception {
serviceA.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceA_NEVER")
public class ServiceA {
@Autowired
ServiceB serviceB;
@Transactional
public void callServiceB() {
System.out.println("ServiceA#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_NEVER")
public class ServiceB {
@Transactional(propagation = Propagation.NEVER)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
Application run failed
Caused by: org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
NESTED #
It works like the default propagation mode (REQUIRES
) if no active transaction exists. It creates a save point and rollback to it if our business logic code throws an exception. Only some transaction managers support this feature.
Scenario | Service A | Service B | A calls B |
---|---|---|---|
13 | REQUIRED | NESTED | Create a save point if the JPA provider supports |
14 | Non-Transactional | NESTED | Same as Scenario 2 |
Example 13:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example13 implements CommandLineRunner {
@Autowired
ServiceA serviceA;
@Override
public void run(String... args) throws Exception {
serviceA.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceA_NESTED")
public class ServiceA {
@Autowired
ServiceB serviceB;
@Transactional
public void callServiceB() {
System.out.println("ServiceA#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_NESTED")
public class ServiceB {
@Transactional(propagation = Propagation.NESTED)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
Application run failed
Caused by: org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities
By default, Spring Boot Data JPA uses Hibernate as the default JPA vendor. NESTED Transaction is not available on Hibernate.
Example 14:
Driver Class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Example14 implements CommandLineRunner {
@Autowired
ServiceWithoutTx service;
@Override
public void run(String... args) throws Exception {
service.callServiceB();
}
}
Service A:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service(value = "ServiceWithoutTx_NESTED")
public class ServiceWithoutTx {
@Autowired
ServiceB serviceB;
public void callServiceB() {
System.out.println("ServiceWithoutTx#callServiceB");
serviceB.run();
}
}
Service B:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(value = "ServiceB_NESTED")
public class ServiceB {
@Transactional(propagation = Propagation.NESTED)
public void run() {
System.out.println("ServiceB#run");
}
}
Some Console Output:
ServiceWithoutTx#callServiceB
Creating new transaction with name [com.caizhenhua.springboot.transaction.propagation.nested.ServiceB.run]: PROPAGATION_NESTED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(963138052<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@434a8938]
ServiceB#run
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(963138052<open>)]
Closing JPA EntityManager [SessionImpl(963138052<open>)] after transaction
- Previous: Spring @Transactional Annotation
- Next: Spring Security with Spring Boot