【Java系列002】正確使用@Transactional註解

你好,我是miniluo,今天我和你聊聊Spring聲明式事務不生效的坑。java

下面就讓我和你一塊兒學習有哪些幾種狀況下Spring聲明式事務不生效的坑。ide

沒有正確理解@Transactional註解學習

你是否曾經寫過和下文相似的代碼?測試

  @Service("productService")public class ProductServiceImpl implements IProductService {@Overridepublic void createProduct(Product product) {//此處可能會實現一些業務邏輯代碼,判斷是否知足建立產品的條件//請求內部方法建立產品 product.setProductName("Spring聲明式事務"); executeCreateProduct(product); }@Transactionalprivate void executeCreateProduct(Product product){if(Objects.isNull(product)){throw new RuntimeException("沒法建立空產品"); } product.create();//模擬出現異常期待事務回滾int zero = 1/0; }}

通過測試,咱們發現拋出了異常,但是產品仍是被建立了。緣由是,Spring 默認經過動態代理的方式實現 AOP,對目標方法進行加強,private 方法沒法代理到,這麼說咱們修改成public方法應該有效果。改完後再嘗試發現產品仍是被建立。這裏不得不說下@Transactional生效必須經過代理過的類從外部調用目標方法才能生效。知道這個緣由後,咱們把ProductServiceImpl以自身的方式注入,調整代碼以下:this

  @Service("productService")public class ProductServiceImpl implements IProductService {@Autowiredprivate ProductServiceImpl self;@Overridepublic void createProduct(Product product) {//此處可能會實現一些業務邏輯代碼,判斷是否知足建立產品的條件 product.setProductName("Spring聲明式事務"); self.executeCreateProduct(product); //請求內部方法建立產品 }@Transactionalpublic void executeCreateProduct(Product product){if(Objects.isNull(product)){throw new RuntimeException("沒法建立空產品"); } product.create();int zero = 1/0; //模擬出現異常期待事務回滾 }}

再次測試,咱們發現產品並無被建立,說明事務已生效,由於self 是由 Spring 經過 CGLIB 方式加強過的類。這種方式雖然能解決,可是並不建議這麼作,而應該把@Transactional寫在接口的實現方法上,建議把查詢和更新(新增)分開。編碼

異常處理不當致使事務失效spa

狀況一:異常沒法從方法傳播出去,致使事務失效代理

 ​​​​​​ @Service("productService")@Slf4jpublic class ProductServiceImpl implements IProductService {@Override@Transactionalpublic void createProduct(Product product) {//此處可能會實現一些業務邏輯代碼,判斷是否知足建立產品的條件 product.setProductName("Spring聲明式事務");try { executeCreateProduct(product); //請求內部方法建立產品 }catch (Exception ex){ log.error("建立產品失敗",ex); } }private void executeCreateProduct(Product product){if(Objects.isNull(product)){throw new RuntimeException("沒法建立空產品"); } product.create();int zero = 1/0; //模擬出現異常期待事務回滾 }}

這種寫法在實際編碼中常犯的錯誤,若要使其生效,只需在catch中增長code

TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();

狀況二:受檢異常致使事務不生效
​​​​​​接口

@Service("productService")
@Slf4j
public class ProductServiceImpl implements IProductService {
@Override
@Transactional
public void createProduct(Product product)
throws IOException {
//此處可能會實現一些業務邏輯代碼,判斷是否知足建立產品的條件
        product.setProductName("Spring聲明式事務");
        executeCreateProduct(product);     //請求內部方法建立產品
    }

private void executeCreateProduct(Product product)
throws IOException {
if(Objects.isNull(product)){
throw new RuntimeException("沒法建立空產品");
        }
        product.create();
        Files.readAllLines(Paths.get("transaction-not-rollback"));
    }
}

或許有這麼個場景,這些代碼並不是你寫的,你並不知道外層或裏面的業務邏輯實現,改動可能會有影響存量功能;這個時候咱們該怎麼辦呢?其實也不難,@Transactional已經爲咱們給出瞭解決方案,只需將@Transactional調整爲

 @Transactional(rollbackFor = Exception.class)

至此,我和你一塊兒回顧了平時編碼過程當中常見的坑,下面再一塊兒來看看關於事務傳播性的坑。

事務傳播性

咱們一塊兒來看下面的兩段代碼,第一段是實現建立產品和產品上架。

 ​​​​​@Service("productService") @Slf4jpublic class ProductServiceImpl implements IProductService {@Autowiredprivate IOfferService offerService;/** * 指望:產品的建立並不會因上架失敗致使回滾 * @param product */@Override@Transactionalpublic void createProduct(Product product){//此處可能會實現一些業務邏輯代碼,判斷是否知足建立產品的條件 product.setProductName("Spring聲明式事務"); executeCreateProduct(product); //請求內部方法建立產品if(Status.UPSHELVE.equals(product.getStatus())){ offerService.productUpShelve(product);//建立產品後,產品隨即上架 } }private void executeCreateProduct(Product product) {if(Objects.isNull(product)){throw new RuntimeException("沒法建立空產品"); } product.create(); }}

第二段是產品上架的實現。

 ​​@Service("offerService")public class OfferServiceImpl implements IOfferService {@Override@Transactionalpublic void productUpShelve(Product product) { product.upShelve();throw new RuntimeException("上架失敗"); }}

咱們的指望是產品的建立成功與否不該該受到上架業務代碼影響,咱們測試後發現,產品上架失敗,致使建立產品回滾;這是什麼緣由呢?這是由於建立產品的事務和產品上架的事務是同一個事務,因此上架失敗天然會致使建立產品回滾。瞭解事務傳播性的同窗都知道,咱們只須要在產品上架的方法配置事務的傳播行爲便可,告訴@Transactional,我是要本身獨立事務

@Transactional(propagation = Propagation.REQUIRES_NEW)

好了,關於Spring聲明式事務常踩的3個坑已經和你一塊兒學習完,期待對你有所幫助和啓發。

思考和討論

一、文中咱們經過注入本身的方式解決了事務不生效的狀況,請問可否使用this來完成呢?this.executeCreateProduct(product); 

二、說到事務的傳播行爲,除了我所說的以外,還有哪些呢?

歡迎留言與我分享和指正!也歡迎你把這篇文章分享給你的朋友或同事,一塊兒交流。

感謝您的閱讀,咱們下節再見!​​​​​​​

關注咱們的公衆號,不定時分享技術、管理、業務等不一樣領域的文章與您一塊兒學習交流。也歡迎投稿:xiaouoron@foxmail.com

​​​​​​​

相關文章
相關標籤/搜索