PROPAGATION_REQUIRED
: 如果存在一個事務,則支援當前事務。如果沒有事務則開啟事務;PROPAGATION_REQUIRES_NEW
:總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起;
問題:
Spring中一個沒有事務的方法A呼叫一個預設事務(PROPAGATION_REQUIRED
)的方法B時,如果使用this呼叫方法B,方法B丟擲RuntimeException
,此時方法B事務未生效,不會回滾。
@Service
public class EmployeeService {
@Autowired
private EmployeeDao employeeDao;
public void save(){
try {
this.saveEmployee(); //此處this呼叫不會開啟事務,資料會被儲存
}catch (Exception e){
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.PROPAGATION_REQUIRED)
//此處無論是PROPAGATION_REQUIRED還是PROPAGATION_REQUIRES_NEW,事務均不生效
public void saveEmployee(){
Employee employee = new Employee();
employee.setName("zhangsan");
employee.setAge("26";
employeeDao.save(employee);
throw new RuntimeException();
}
}
複製程式碼
問題原因:
JDK的動態代理。只有被動態代理直接呼叫時才會產生事務。在SpringIoC容器中返回的呼叫的物件是代理物件而不是真實的物件。而這裡的this是EmployeeService
真實物件而不是代理物件。
解決辦法:
方法1、在方法A上開啟事務,方法B不用事務或預設事務,並在方法A的catch中throw new RuntimeException();
(在沒指定rollbackFor時,預設回滾的異常為RuntimeException
),這樣使用的就是方法A的事務。(一定要throw new RuntimeException();
否則異常被捕捉處理,同樣不會回滾。)如下:
@Transactional() //開啟事務
public void save(){
try {
this.saveEmployee(); //這裡this呼叫會使事務失效,資料會被儲存
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException();
}
}
複製程式碼
方法2、方法A上可以不開啟事務,方法B上開啟事務,並在方法A中將this呼叫改成動態代理呼叫(AopContext.currentProxy()
),如下:
public void save(){
try {
EmployeeService proxy =(EmployeeService) AopContext.currentProxy();
proxy.saveEmployee();
}catch (Exception e){
e.printStackTrace();
}
}
複製程式碼