問題闡述及解決過程
現在需要寫一個定時任務,其處理過程要在for循環進行,並且循環體中要調用兩個方法,大致代碼是這樣的:
1 public void regionRecoveryProtectionPeriod() { 2 for (????) { 3 try { 4 method1(); 5 method2(); 6 } catch (Exception e){ 7 log.error("",e.getMessage()); 8 } 9 } 10 }
用try將循環體包裹起來防止循環中斷,但是這段存在一個問題——循環體中沒有用事務管理,這將會造成很嚴重的后果。
很容易想到的方法就是將循環體單獨寫到一個方法里,將這個新的方法上加事務,代碼如下:
1 public void regionRecoveryProtectionPeriod() { 2 for (????) { 3 try { 4 this.releaseOrigin(); 5 } catch (Exception e){ 6 log.error("",e.getMessage()); 7 } 8 } 9 10 @Transactional(rollbackFor = Exception.class) 11 void releaseOrigin() { 12 method1(); 13 method2(); 14 }
這段代碼看似沒有問題,但是經過試驗,發現事務並沒有起作用,和原來的結果沒有任何區別。
原因是自調用不走代理對象,所以用this.調用內層方法時注解是不生效的,因此要通過代理對象的方式調用內層方法:Object proxy = AopContext.currentProxy();
代碼如下:
public void regionRecoveryProtectionPeriod() { Service proxy = (Service)AopContext.currentProxy(); for (????) { try { proxy.releaseOrigin(); } catch (Exception e){ log.error("",e.getMessage()); } } @Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class) void releaseOrigin() { method1(); method2(); }
這樣做的話,內部事務就能正常生效了,由於外層的方法通常也是要加事務的,所以內層的事務要加上propagation = Propagation.NESTED讓內層事務不會影響到外層事務。
總結
關於這種問題的解決方案有人說可以將內層事務的方法寫在別的service里,或者自己注入自己。代理對象的方法里用了ThreadLocal里的get方法,不知道是否會對性能有啥影響。如果有更好的方法的話可以交流一下
