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();
    }
}

Related articles: