Transactional default exception rollback and transaction propagation behavior
It is convenient to use Spring to manage database transactions by simply introducing the @Transactional annotation in the proxy object to enable transactions. Transactional Transactional (set rollbackFor); Transactional Transactional (set Propagation) By default, runtimeExceptions and errors are rolled back. The default transaction propagation behavior is to support the current transaction or create a new transaction if none exists. The default behaviors above can cover a large number of application scenarios and are convenient to use. You can adapt to a large number of application scenarios without setting parameters. However, when more than two services use transactions, you need to know more about transaction propagation behavior.
Transaction propagation behavior defined in Spring
In the Spring in the org. Springframework. Transaction. TransactionDefinition defined in the transaction propagation behavior, a total of seven kinds of behavior, can be divided into three categories according to support the current transaction or not.
- Support current transactions
PROPAGATION_REQUIRED
: If a transaction currently exists, it is used. If there is no transaction currently, a new transaction is created.PROPAGATION_SUPPORTS
: If a transaction currently exists, it is used, if no transaction currently exists, it is still run in a non-transactional manner.PROPAGATION_MANDATORY
: Uses the transaction if it currently exists, and throws an exception if it does not
- Current transactions are not supported
PROPAGATION_REQUIRES_NEW
: Always create a new transaction and suspend the current transaction if one existsPROPAGATION_NOT_SUPPORTED
: runs nontransactionally, suspending the current transaction if one existsPROPAGATION_NEVER
: runs transactionally, throwing an exception if a transaction is currently in place
- The nested transaction
PROPAGATION_NESTED
: Creates a nested transaction to run. If no transaction exists, the behavior is the samePROPAGATION_REQUIRED
The above is the description of the seven transaction propagation behaviors, which will be abstract and not easy to understand. The following is a demonstration and description of the three common transaction propagation behaviors REQUIRED,REQUIRED_NEW, and NESTED service combinations.
Examples of use of common transaction propagation behavior
1 Upstream services REQUIRED, downstream services do not add transactions
The upstream service
@Transactional(rollbackFor = Exception.class)
public void createUser(a) {
User user = new User();
user.setAge(19).setName("test1").setVersion(1);
userMapper.insert(user);
logService.saveLog("createUser");
}
Copy the code
The downstream service throws an exception:
public void saveLog(String method) {
SystemLog entity = new SystemLog();
entity.setMethod(method);
systemLogMapper.insert(entity);
throw new RuntimeException("Test rollback saveLog without adding transaction,createUser Propagation.REQUIRED");
}
Copy the code
The current transaction takes effect and the current service createUser and the downstream service saveLog are rolled back.
2 The current service REQUIRED and the downstream service REQUIRES_NEW
2.1 Downstream Services Throw exceptions
The code of the upstream service is unchanged. The code of the downstream service is modified as follows
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String method) {
SystemLog entity = new SystemLog();
entity.setMethod(method);
systemLogMapper.insert(entity);
throw new RuntimeException("Test rollback saveLog Propagation.REQUIRES_NEW,createUser Propagation.REQUIRED");
}
Copy the code
The current transaction takes effect and the current service createUser and the downstream service saveLog are rolled back. The transaction of the current service is suspended, the downstream transaction is rolled back, and exceptions can cause the upstream transaction to be rolled back. So it’s still going to roll back.
2.2 The upstream Service Throws an exception
Put the operation that threw the exception into the upstream service, and the downstream service returns to its normal logic, modifying the code as follows
@Transactional
public void createUser(a) {
User user = new User();
user.setAge(19).setName("test1").setVersion(1);
userMapper.insert(user);
logService.saveLog("createUser");
throw new RuntimeException("Upstream service createUser Propagation.REQUIRED Raises an exception" +
"Downstream service saveLog REQUIRES_NEW");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String method) {
SystemLog entity = new SystemLog();
entity.setMethod(method);
systemLogMapper.insert(entity);
}
Copy the code
[saveLog] [saveLog] [saveLog] [saveLog] [saveLog] [saveLog] [saveLog] The reason is that when the downstream transaction is configured as REQUIRES_NEW to execute the downstream service code, the current transaction is suspended, the downstream transaction executes, the downstream transaction does not have any exception, executes smoothly, returns to the upstream transaction, the upstream transaction encounters an exception, and the upstream transaction rolls back.
A downstream transaction uses REQUIRES_NEW, and the upstream and downstream are two separate transactions. A downstream commit failure does not directly affect the upstream transaction, except in the first case, when the downstream service throws an exception, it is also thrown to the upstream service, and the upstream service is rolled back.
3 USES REQUIRES_NEST
Scenarios that use nested transactions have two requirements:
- Successful federation: The upstream transaction is required to commit with the downstream transaction, that is, the downstream transaction is committed only if the upstream transaction successfully commits. this
PROPAGATION_REQUIRED
It can be done. - Isolation failure: Rollback of the downstream transaction is required without affecting the commit of the upstream transaction. This requirement is simply called “quarantine failure.” this
PROPAGATION_REQUIRES_NEW
It can be done.
If requirement 1 is met with PROPAGATION_REQUIRED, the rollback of the downstream transaction unconditionally rolls back the upstream transaction. If requirement 2 is not met, the commit failure of the downstream transaction does not affect the upstream transaction.
Requirement 2 is satisfied with PROPAGATION_REQUIRES_NEW, but the downstream transaction is a completely new transaction context, and the success of the upstream transaction does not affect the downstream commit at all, which does not satisfy Requirement 1.
Need to use NESTED to meet upstream affects downstream, downstream does not affect upstream
Code examples:
3.1 Upstream services use the PROPAGATION_REQUIRED mechanism to throw exceptions, and downstream services use the NESTED mechanism
The upstream service@Transactional
public void createUser(a) {
User user = new User();
user.setAge(19).setName("test1").setVersion(5);
userMapper.insert(user);
logService.saveLog("createUser");
throw new RuntimeException("Upstream throws exceptions, downstream NESTED mechanism"); } Downstream service@Transactional(propagation = Propagation.NESTED)
public void saveLog(String method) {
SystemLog entity = new SystemLog();
entity.setMethod(method);
systemLogMapper.insert(entity);
}
Copy the code
If upstream rolls back, downstream rolls back.
3.2 Upstream services use the PROPAGATION_REQUIRED mechanism, and downstream services use the NESTED mechanism to throw exceptions
The upstream service@Transactional
public void createUser(a) {
User user = new User();
user.setAge(19).setName("test1").setVersion(5);
userMapper.insert(user);
try {
logService.saveLog("createUser");
}catch(Exception e) {}} downstream service@Transactional(propagation = Propagation.NESTED)
public void saveLog(String method) {
SystemLog entity = new SystemLog();
entity.setMethod(method);
systemLogMapper.insert(entity);
throw new RuntimeException("Downstream throws exceptions using the NESTED mechanism");
}
Copy the code
The upstream commit is normal and the downstream rollback does not affect the upstream commit.
conclusion
Here are seven propagation behaviors for Spring transactions, divided into three categories that support current transactions: PROPAGATION_REQUIRED,PROPAGATION_SUPPORTS,PROPAGATION_MANDATORY The following transactions are not supported :PROPAGATION_REQUIRES_NEW,PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER, and NESTED transactions :NESTED. PROPAGATION_REQUIRE PROPAGATION_REQUIRES_NEW Describes the three most common propagating behaviors