A Cause of Spring Transaction Failure on this Call
- 2021-12-05 06:02:33
- OfStack
PROPAGATION_REQUIRED
If 1 transaction exists, the current transaction is supported. If there is no transaction, open the transaction;
PROPAGATION_REQUIRES_NEW
Always opens a new transaction. If 1 transaction already exists, the existing transaction is suspended;
问题:
A no-transaction method in Spring A calls a default transaction (
PROPAGATION_REQUIRED
If the method B is called with this, the method B throws
RuntimeException
At which point the method B transaction does not take effect and will not be rolled back.
@Service
public class EmployeeService {
@Autowired
private EmployeeDao employeeDao;
public void save(){
try {
this.saveEmployee(); // Here this The call does not open the transaction, and the data is saved
}catch (Exception e){
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.PROPAGATION_REQUIRED)
// Whether it is here, PROPAGATION_REQUIRED Or PROPAGATION_REQUIRES_NEW None of the transactions take effect
public void saveEmployee(){
Employee employee = new Employee();
employee.setName("zhangsan");
employee.setAge("26";
employeeDao.save(employee);
throw new RuntimeException();
}
}
问题原因:
Dynamic proxy for JDK. Transactions are generated only when they are directly invoked by dynamic proxies. The invoked object returned in the SpringIoC container is a proxy object rather than a real object. And this here is
EmployeeService
Real object instead of proxy object.
解决办法:
Method 1, the transaction is started on the method A, the method B does not need the transaction or the default transaction, and in the catch of the method A
throw new RuntimeException();
(When rollbackFor is not specified, the default exception to roll back is
RuntimeException
), which uses the transaction of method A. (1 must
throw new RuntimeException();
Otherwise, the exception is caught and handled, and it will not be rolled back.) Here is:
@Transactional() // Open a transaction
public void save(){
try {
this.saveEmployee(); // Here this The call invalidates the transaction and the data is saved
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException();
}
}
Method 2. No transaction can be opened on method A, transaction can be opened on method B, and this call can be changed into dynamic proxy call in method A (
AopContext.currentProxy()
), as follows:
public void save(){
try {
EmployeeService proxy =(EmployeeService) AopContext.currentProxy();
proxy.saveEmployee();
}catch (Exception e){
e.printStackTrace();
}
}