處理springboot 下提交事務異常,數據庫沒有回滾的問題。
spring的文檔中說道,spring聲明式事務管理默認對非檢查型異常和運行時異常進行事務回滾,而對檢查型異常則不進行回滾操作。
什么是檢查型異常什么又是非檢查型異常?
最簡單的判斷點有兩個:
1.繼承自runtimeexception或error的是非檢查型異常,而繼承自exception的則是檢查型異常(當然,runtimeexception本身也是exception的子類)。
2.對非檢查型類異常可以不用捕獲,而檢查型異常則必須用try語句塊進行處理或者把異常交給上級方法處理總之就是必須寫代碼處理它。所以必須在service捕獲異常,然后再次拋出,這樣事務方才起效。
結論:
在spring的事務管理環境下,使用unckecked exception可以極大地簡化異常的處理,只需要在事務層聲明可能拋出的異常(這里的異常可以是自定義的unckecked exception體系),在所有的中間層都只是需要簡單throws即可,不需要捕捉和處理,直接到最高層,比如UI層再進行異常的捕捉和處理。
默認規則:
1 讓checked例外也回滾: @Transactional(rollbackFor=Exception.class),一般只需添加這個即可
2 讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)
3 不需要事務管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED),或者不添加
注意: 如果異常被try{}catch{}了,事務就不回滾了,如果想讓事務回滾必須再往外拋try{}catch{throw Exception}。因為一旦你try{}catch{}了。系統會認為你已經手動處理了異常,就不會進行回滾操作。
示例:

1 @Override 2 @Transactional(rollbackFor = Exception.class) 3 public Integer submitOrder(FlashbuyOrder flashbuyOrder, List<FlashbuyOrderItem> itemList) 4 throws Exception 5 { 6 7 return null; 8 9 }
spring 事務控制 設置手動回滾 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
https://www.cnblogs.com/liuzhenlei/p/6777644.html

1 //假設這是一個service類的片段 2 3 try{ 4 //出現異常 5 } catch (Exception e) { 6 e.printStackTrace(); 7 //設置手動回滾 8 TransactionAspectSupport.currentTransactionStatus() 9 .setRollbackOnly(); 10 } 11 //此時return語句能夠執行 12 return xxx;
如上:
當我們需要在事務控制的service層類中使用try catch 去捕獲異常后,就會使事務控制失效,因為該類的異常並沒有拋出,就不是觸發事務管理機制。怎樣才能即使用try catch去捕獲異常,而又讓出現異常后spring回滾呢,這里就要用到
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
完美解決問題。並且能夠使該方法執行完。
這個需要注意兩點:
1. 方法上要加上 @Transactional(rollbackFor = Exception.class) 再配合TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 才可以,
否則會報錯 NoTransactionException: No transaction aspect-managed TransactionStatus in scope
at

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ) public void testInsert(int index) throws Exception { OrderDO orderDO = new OrderDO(); orderDO.setThirdOrderId("1000"); int rowCount = this.orderMapper.insertOrder(orderDO); if (index == 1) { throw new RuntimeException("wrong"); } if (index == 2) { throw new Exception("wrong"); } if (index == 3) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }
2. Spring Transactional一直是RD的事務神器,但是如果用不好,反會傷了自己。下面總結@Transactional經常遇到的幾個場景:

@Transactional 加於private方法, 無效
@Transactional 加於未加入接口的public方法, 再通過普通接口方法調用, 無效
@Transactional 加於接口方法, 無論下面調用的是private或public方法, 都有效
@Transactional 加於接口方法后, 被本類普通接口方法直接調用, 無效
@Transactional 加於接口方法后, 被本類普通接口方法通過接口調用, 有效
@Transactional 加於接口方法后, 被它類的接口方法調用, 有效
@Transactional 加於接口方法后, 被它類的私有方法調用后, 有效
Transactional是否生效, 僅取決於是否加載於接口方法, 並且是否通過接口方法調用(而不是本類調用)。
https://segmentfault.com/a/1190000014617571

ProductService.java /**********************************************************************/ public interface ProductService{ Integer getPrice(ProductInfo p); Integer compute(ProductInfo p); } /**********************************************************************/ ProductServiceImpl.java /**********************************************************************/ @Service public class ProductServiceImpl implements ProductService{ @Autowired private ProductService productService; public Integer getPrice(ProductInfo p){ productService.compute(p); } @Transactional(rollbackFor = Exception.class) public Integer compute(ProductInfo p){ try{ ... }catch(Exception e){ e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return 0; } } } 是可以的,因為是通過接口調用了

ProductService.java /**********************************************************************/ public interface ProductService{ Integer getPrice(ProductInfo p); } public interface MyService{ Integer compute(ProductInfo p); } /**********************************************************************/ ProductServiceImpl.java /**********************************************************************/ @Service public class ProductServiceImpl implements ProductService{ @Autowired private MyService myService ; public Integer getPrice(ProductInfo p){ myService.compute(p); } } @Service public class MyServiceImpl implements MyService{ @Transactional(rollbackFor = Exception.class) public Integer compute(ProductInfo p){ try{ ... }catch(Exception e){ e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return 0; } } } 放在兩個接口類里面,然后通過接口方法調用,是可以生效的
3.在web項目中,很多時候要用到@Transactional 注解方法或者類進行事務處理,自動事務提交有時候就會有問題,這個時候就要用到手動進行事務提交 ,在try catch 異常拋出里面手動回滾事務處理TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
使用Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint(); 設置回滾點,
使用TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);回滾到savePoint。
轉自:
https://blog.csdn.net/zhuchunyan_aijia/article/details/80191534
https://blog.csdn.net/xuhaogang3/article/details/82190026