How to use @Transactional when calling methods in a business scenario loop

need

Taking the e-commerce scenario as an example, assuming that the main order contains multiple sub-orders, some sub-orders may not be completed normally when the main order is submitted, and the sub-orders that are normally completed are required to perform the corresponding database operations normally, and the sub-orders that cannot be completed are rolled back The database operation performed.

Review of Technical Points

declarative transaction

At present, the commonly used transaction rollback method in the Spring framework is to add @Transactional to the method, which is "annotation-based declarative transaction".

Commonly used attributes in @Transactional:

Attributes type describe
Attributes type describe
value String An optional qualification descriptor specifying the transaction manager to use
propagation enum: Propagation Optional transaction propagation behavior settings
isolation enum: Isolation Optional transaction isolation level settings
readOnly boolean Read-write or read-only transactions, default read-write
timeout int (in seconds granularity) Transaction timeout setting
rollbackFor Array of Class objects, must inherit from Throwable An array of exception classes that caused the transaction to be rolled back
rollbackForClassName An array of class names, must inherit from Throwable An array of exception class names that caused the transaction to be rolled back
noRollbackFor Array of Class objects, must inherit from Throwable An array of exception classes that will not cause the transaction to be rolled back
noRollbackForClassName An array of class names, must inherit from Throwable An array of exception class names that will not cause transaction rollback

Among them, the optional values ​​of the propagation attribute are:

  • propagation represents the propagation behavior of the transaction, the default value is Propagation.REQUIRED, other attribute information is as follows:

  • Propagation.REQUIRED: If there is a current transaction, join the transaction, if there is no current transaction, create a new transaction.

  • Propagation.SUPPORTS: If there is a transaction currently, join the transaction; if there is no transaction currently, continue running in a non-transactional manner.

  • Propagation.MANDATORY: If there is a transaction currently, join the transaction; if there is no transaction currently, throw an exception.

  • Propagation.REQUIRES_NEW: Re-create a new transaction, if there is a current transaction, suspend the current transaction.

  • Propagation.NOT_SUPPORTED: Run in a non-transactional manner. If there is a current transaction, suspend the current transaction.

  • Propagation.NEVER: Run in a non-transactional manner, and throw an exception if there is a current transaction.

  • Propagation.NESTED : Same effect as Propagation.REQUIRED.

programmatic transaction

The programmatic transaction method requires developers to manually manage transaction opening, submission, rollback and other operations in the code, which is highly intrusive to the code.

Solution 1 (not feasible)

At the beginning, the author tested locally that this method was feasible, but when I ran it again, I reported an error:
org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope,
after checking, the parent method does not go through the proxy when calling the same sub-method, and the @Transactional annotation on the sub-method is naturally invalid of. Therefore, this method is not feasible and is for reference only.


public void executeMainOrder() {
    
    
	for (Order subOrder: subOrderList){
    
    
		try {
    
    
			executeSubOrder(subOrder);
		} catch(Exception e){
    
    
			// 子订单执行失败则记录相关参数
			log(e, subOrder);
		}
	}
}

// 每个子订单采用独立的事务进行入库操作
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void executeSubOrder(Order subOrder){
    
    
	try {
    
    
		// 执行入库操作
		baseMapper.insert(subOrder);
		// 执行业务操作
		handleOrder(subOrder);
	} catch (Exception e){
    
    
		// 手动回滚当前子订单所处的事务
		TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		// 抛出自定义异常
		throw new ServiceException("");
	}
}

Solution 2 (feasible)

The solution is also very simple. Since the calling method of the same kind leads to not going through the proxy, it is better to divide the method into other classes and call it. Therefore, the author chooses to extract the sub-methods into a class separately. After adding the relevant annotations, the parent method will be Can be called successfully.

// A类
@Autowire
BService bService;
public void executeMainOrder() {
    
    
	for (Order subOrder: subOrderList){
    
    
		try {
    
    
			bService.executeSubOrder(subOrder);
		} catch(Exception e){
    
    
			// 子订单执行失败则记录相关参数
			log(e, subOrder);
		}
	}
}

// B类
// 每个子订单采用独立的事务进行入库操作
@Transactional(rollbackFor = "Exceptioin.class", propagation = Propagation.REQUIRES_NEW)
private void executeSubOrder(Order subOrder){
    
    
	try {
    
    
		// 执行入库操作
		baseMapper.insert(subOrder);
		// 执行业务操作
		handleOrder(subOrder);
	} catch (Exception e){
    
    
		// 抛出自定义异常
		throw new ServiceException("");
	}
}

reference article

https://www.cnblogs.com/better-farther-world2099/articles/14982412.html
https://www.cnblogs.com/process-h/p/16777472.html
https://www.jianshu.com/p/00758c77bf60

Guess you like

Origin blog.csdn.net/Ka__ze/article/details/131809244