使用默認的事務處理方式
因為在java的設計中,它認為不繼承RuntimeException的異常是”checkException”或普通異常,如IOException,這些異常在java語法中是要求強制處理的。對於這些普通異常,spring默認它們都已經處理,所以默認不回滾。可以添加rollbackfor=Exception.class來表示所有的Exception都回滾。
內部調用
不帶事務的方法調用該類中帶事務的方法,不會回滾。因為spring的回滾是用過代理模式生成的,如果是一個不帶事務的方法調用該類的帶事務的方法,直接通過this.xxx()
調用,而不生成代理事務,所以事務不起作用。常見解決方法,拆類。
spring中在一個擁有事務的方法A中調用另一個會掛起事務並創建新事務的方法B,如果使用this調用這個方法B,
此時方法B拋出了一個一場,此時的方法B的事務會失效的。並不會回滾。
PROPAGATION_REQUIRED
: 如果存在一個事務,則支持當前事務。如果沒有事務則開啟事務;PROPAGATION_REQUIRES_NEW
:總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。
如下:
@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(); } }