In Spring Boot development,TransactionsIt is a crucial concept, especially when it involves multi-layer business logic or multiple database operations. Spring provides powerful transaction management functions, allowing developers to easily control the behavior of transactions.Transaction propagation mechanismAs part of Spring transaction management, it is a very important concept in Spring transaction management.
This article will introduce the principles of transaction propagation mechanism in Spring Boot and its commonly used configurations to help developers better understand how transaction propagation works.
1. What is the transaction communication mechanism?
Transaction propagation mechanismDefines how the behavior of a transaction is propagated when a transaction is called in multiple methods. In other words, it determines how a transaction method should handle the operation of opening, committing, rolling back and other transactions when it is called by another method.
Transaction communication mechanism passed@Transactional
Annotatedpropagation
Configuration of attributes, it has multiple communication behaviors, and developers can choose the appropriate communication method according to specific needs. Common transmission behaviors include:
REQUIRED
REQUIRES_NEW
SUPPORTS
MANDATORY
NOT_SUPPORTED
NEVER
NESTED
2. The propagation behavior of Spring transaction propagation mechanism
1. REQUIRED (default propagation behavior)
Communication behavior:If there is currently no transaction, a new transaction is created; if there is currently a transaction, it is added to the existing transaction.
Application scenarios:This is the most commonly used propagation behavior, which is usually used in business method calls to ensure consistency of the calling methods.
@Transactional(propagation = ) public void methodA() { // Business logic} @Transactional(propagation = ) public void methodB() { // Business logic}
- if
methodA()
There is no transaction, it will create a new transaction; - if
methodA()
Already in a transaction, thenmethodB()
Will join this transaction.
2. REQUIRES_NEW
Communication behavior:Always create a new transaction. If there is a transaction currently, the current transaction is suspended and the current transaction will be restored after the new transaction is committed or rolled back.
Application scenarios:When we want a method to be processed independently of the current transaction, it is usually used for operations that do not want to be affected by external transactions, such as logging, notifications, etc.
@Transactional(propagation = Propagation.REQUIRES_NEW) public void methodC() { // Business logic}
- Regardless
methodC()
Whether there is a transaction when calling will open a new transaction; - if
methodC()
There is already a transaction when the call is called, and it will suspend the current transaction and start a new transaction; - when
methodC()
After the transaction is completed, the originally pending transaction will resume execution.
3. SUPPORTS
Communication behavior:If there is currently a transaction, it is added to the existing transaction; if there is currently no transaction, it is executed in a non-transactional manner.
Application scenarios:When the method supports transactions but does not force the transaction to exist, it can be usedSUPPORTS
. For example, some methods may not require transactions, but if there is a transaction, they will join in.
@Transactional(propagation = ) public void methodD() { // Business logic}
- if
methodD()
There is already a transaction when the call is called, and it will join the transaction; - If there is no transaction,
methodD()
Execute in a non-transactional manner.
4. MANDATORY
Communication behavior:If there is currently a transaction, it is added to the existing transaction; if there is no transaction, an exception is thrown.
Application scenarios:If the method relies on transaction execution but does not want to create transactions on its own, you can useMANDATORY
. If there is no existing transaction, it will be thrownTransactionRequiredException
Exception.
@Transactional(propagation = ) public void methodE() { // Business logic}
- If there is no transaction at present,
methodE()
An exception will be thrown; - If there is currently a transaction,
methodE()
Will be added to the transaction.
5. NOT_SUPPORTED
Communication behavior:If there is currently a transaction, the current transaction is suspended and the method is executed in a non-transactional manner.
Application scenarios:When a method does not want to participate in transaction operations, you can useNOT_SUPPORTED
, for example, some query operations, they do not require transaction support.
@Transactional(propagation = Propagation.NOT_SUPPORTED) public void methodF() { // Business logic}
- If there is currently a transaction,
methodF()
The current transaction will be suspended and the transaction will not be supported during execution; - If there is no transaction,
methodF()
Execute in a non-transactional manner.
6. NEVER
Communication behavior:If there is currently a transaction, an exception is thrown; if there is no transaction, it is executed in a non-transactional manner.
Application scenarios:When a method is not allowed to be used when running in a transaction. For example, some specific checking methods require that the transaction does not exist at all.
@Transactional(propagation = ) public void methodG() { // Business logic}
- If there is currently a transaction,
methodG()
Will be thrownIllegalTransactionStateException
Exception; - If there is no transaction,
methodG()
Execute in a non-transactional manner.
7. NESTED
Communication behavior:If there is currently no transaction, create a new transaction; if there is currently a transaction, nest a transaction in the current transaction. Nested transactions can be committed independently or rolled back.
Application scenarios:If you want transactions to be nested and will not affect the commit of external transactions when nested transactions are rolled back, you can useNESTED
。
@Transactional(propagation = ) public void methodH() { // Business logic}
- if
methodH()
Exception is thrown internally and rolled back will not affect external transactions; - if
methodH()
If submitted successfully, external transactions will also be submitted.
3. Analysis of the principle of transaction communication mechanism
1. The principle of transaction propagation
Spring's transaction propagation mechanism is actually implemented through AOP (sectional-oriented programming). Spring generates a proxy object (usually a JDK dynamic proxy or a CGLIB proxy) at runtime. When the transaction method is executed, the proxy is responsible for judging the propagation behavior of the transaction and determining whether to start a new transaction or join an existing transaction based on the behavior.
- Transaction starts: When the method is executed, the agent will check whether there is a transaction already. If not, a new transaction needs to be created based on the propagation behavior.
-
Transaction nesting:For
REQUIRES_NEW
orNESTED
Propagate behavior, Spring will create new transactions that are independent of external transactions. - Transaction rollback: If an exception occurs in the method and a rollback rule is specified, the proxy rolls back the transaction.
- Transaction Submission: When the method is executed successfully, Spring will submit the transaction.
2. The order of execution of transaction propagation mechanism
Assume that method A calls method B, method B usesREQUIRES_NEW
Communication behavior:
- When method A starts execution, determine whether there is a transaction. If there is no transaction, the transaction will be started.
- Method A calls method B. Method B will pause the transaction of method A and start its own transaction.
- After the execution of method B is completed, submit your own transaction and restore the transaction of method A.
This is how the transaction propagation mechanism behaves in nested calls.
In Spring, the implementation of the transaction propagation mechanism relies on AOP (sectional programming), and AOP is only applied to beans managed through Spring. If we call methods in the same class directly (i.e. methods of the same instance), the transaction propagation mechanism may fail because Spring's proxy objects are not applied to these internal method calls. The following are some code examples about the transaction propagation mechanism and will show scenarios where the transaction propagation mechanism fails.
4. Code test examples
1. REQUIRED propagation behavior example
@Service public class UserService { @Transactional(propagation = ) public void methodA() { ("methodA: Start a transaction"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodA: Complete transaction"); } @Transactional(propagation = ) public void methodB() { ("methodB: Start a transaction"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodB: Complete the transaction"); } public void testRequiredPropagation() { methodA(); // This will trigger the transaction's propagation mechanism methodB(); // It will also be added to the current transaction } }
Expected output:
methodA: Start a transaction
methodA: Complete the transaction
methodB: start a transaction
methodB: Complete the transaction
2. REQUIRES_NEW propagation behavior example
@Service public class UserService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodC() { ("methodC: Start a new transaction"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodC: Complete new transaction"); } public void testRequiresNewPropagation() { methodC(); // New affairs will be executed independently } }
Expected output:
methodC: Start a new transaction
methodC: Complete new transactions
3. Example of SUPPORTS propagation behavior
@Service public class UserService { @Transactional(propagation = ) public void methodD() { ("methodD: Support transaction (if any)"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodD: done"); } public void testSupportsPropagation() { methodD(); // If a transaction exists, the method will be added to the current transaction } }
Expected output:
If there is no transaction:
methodD: Support transactions (if any)
methodD: Completed
If there is a transaction:
methodD: Support transactions (if any)
methodD: Completed
4. MANDATORY Propagation Behavior Example
@Service public class UserService { @Transactional(propagation = ) public void methodE() { ("methodE: Must join the transaction"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodE: Complete the transaction"); } public void testMandatoryPropagation() { methodE(); // There must be a transaction when calling, otherwise an exception will be thrown } }
Expected output:
- If there is no transaction, throw
TransactionRequiredException
Exception; - If there is a transaction, the method will join the existing transaction.
5. NOT_SUPPORTED propagation behavior example
@Service public class UserService { @Transactional(propagation = Propagation.NOT_SUPPORTED) public void methodF() { ("methodF: The current transaction is suspended"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodF: done"); } public void testNotSupportedPropagation() { methodF(); // If there is a transaction, it will be suspended and non-transactional operations will be performed } }
Expected output:
If the method is called in a transaction, the transaction will be suspended and non-transactional operations are performed:
methodF: The current transaction is suspended
methodF: Completed
6. NESTED propagation behavior example
@Service public class UserService { @Transactional(propagation = ) public void methodG() { ("methodG: Start nested transactions"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodG: Complete nested transactions"); } public void testNestedPropagation() { methodG(); // The method starts a nested transaction } }
Expected output:
methodG: Start nested transactions
methodG: Complete nested transactions
5. Scenarios of failure of transaction communication mechanism
Scenario 1: Direct call of methods in the same class
If we call another directly in an instance of one class,@Transactional
The annotated method, the transaction propagation mechanism may fail because the transaction proxy is based on Spring AOP, and AOP only works for external method calls.
Example: Transaction invalidation
@Service public class UserService { @Transactional(propagation = ) public void methodA() { ("methodA: Start a transaction"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodA: Complete transaction"); } public void methodB() { ("methodB: The transaction does not take effect (directly called)"); methodA(); // Call methodA() directly, the transaction will not be propagated } }
question:
methodB()
Will call directlymethodA()
, but because transaction annotations rely on AOP proxy,methodB()
Not called through Spring proxymethodA()
, so the transaction will not take effect.
Expected output:
methodB: The transaction does not take effect (directly called)
methodA: Start a transaction
methodA: Complete the transaction
The transaction should bemethodA()
is effective, but because it is a direct call, it does not take effect.
Solution
In order for the transaction propagation mechanism to take effect, the method should be called through the proxy object in the Spring container, which can be done through@Autowired
Inject the current class instance and call its methods, or indirectly by using an external class instance.
@Service public class UserService { @Autowired private UserService self; // Inject the proxy instance of the current class @Transactional(propagation = ) public void methodA() { ("methodA: Start a transaction"); // Simulate database operations try { (1000); // Simulation time-consuming operation } catch (InterruptedException e) { (); } ("methodA: Complete transaction"); } public void methodB() { ("methodB: The transaction will take effect (called through proxy)"); (); // Call methodA() via proxy } }
6. Summary
Spring Boot's transaction propagation mechanism provides developers with flexible transaction management methods, ensuring that transaction behavior can be accurately controlled in complex business logic. By reasonably selecting transaction propagation behavior, we can achieve transaction consistency and isolation in multi-layer business logic.
- REQUIRED: In most cases, this propagation behavior is used to ensure transaction consistency.
- REQUIRES_NEW: Used when independent transactions are required.
- SUPPORTSandNOT_SUPPORTED: Used when the method supports or does not support transactions.
- MANDATORYandNEVER: Strictly control the participation of transactions.
- NESTED: Supports nested transactions, can be committed and rolled back independently.
This is the article about a deep understanding of the SpringBoot transaction communication mechanism. For more related SpringBoot transaction communication content, please search for my previous article or continue browsing the related articles below.