天天看點

spring中常見事務失效處理,總結、分析

spring中常見事務失效處理分析

  • ​​寫在前面​​
  • ​​一、事務基礎​​
  • ​​1.1、定義:​​
  • ​​1.2、四大特性(A-C-I-D)​​
  • ​​1.3、常用事務管理​​
  • ​​二、Spring 事務​​
  • ​​2.1、Spring中事務基礎​​
  • ​​2.2、要注意的地方​​
  • ​​2.2、常見異常​​
  • ​​2.3、Spring中常用事務處理操作 / 場景,要注意的地方​​
  • ​​2.3.1、 捕捉異常, 導緻不能復原​​
  • ​​2.3.2、 自定義異常,導緻事務不復原​​
  • ​​2.3.3、 嵌套方法​​
  • ​​2.3.4、 同一個類中, 一個不标注事務的方法去調用 transactional 的方法, 事務會失效​​
  • ​​三、Mysql 事務​​
  • ​​四、Oracle 事務​​
  • ​​五、Nosql 事務​​
  • ​​六、分布式事務​​
  • ​​6.1、出現原因​​
  • ​​6.2、JTA​​
  • ​​七、總結​​

寫在前面

隻是總結,分析

一、事務基礎

1.1、定義:

資料庫幾乎是所有系統的核心子產品,它将資料有條理地儲存在儲存媒體(磁盤)中,

并在邏輯上,将資料以結構化的形态呈現給使用者。支援資料的增、删、改、查,并在過程中保障資料的正确且可靠。

1.2、四大特性(A-C-I-D)

  • 原子性(Atomicity): 事務要麼全部完成,要麼全部取消。 如果事務崩潰,狀态回到事務之前(事務復原)。
  • 隔離性(Isolation): 如果2個事務 T1 和 T2 同時運作,事務 T1 和 T2 最終的結果是相同的,不管 T1和T2誰先結束。
  • 持久性(Durability): 一旦事務送出,不管發生什麼(崩潰或者出錯),資料要儲存在資料庫中。
  • 一緻性(Consistency): 隻有合法的資料(依照關系限制和函數限制)才能寫入資料庫。

場景分析

如何同時保證上述交易中,A賬戶總金額減少一個億,B賬戶總金額增加一個億? A

A賬戶如果同時在和C賬戶交易(T2),如何讓這兩筆交易互不影響? I

如果交易完成時資料庫突然崩潰,如何保證交易資料成功儲存在資料庫中? D

如何在支援大量交易的同時,保證資料的合法性(沒有錢憑空産生或消失) ? C

1.3、常用事務管理

主要包括,以下幾種

  • Mysql 事務
  • Oracle 事務
  • Nosql 事務
  • Spring 事務
  • 分布式 事務

二、Spring 事務

2.1、Spring中事務基礎

Spring中事務基礎很簡單,兩種方式配置事務,XML或者注解方式,以下基于注解方式,介紹

實作原理:基于Spring AOP,可自行學習

七種事務機制

1. TransactionDefinition.PROPAGATION_REQUIRED:
   如果目前存在事務,則加入該事務;如果目前沒有事務,則建立一個新的事務。這是預設值。
 
2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:
   建立一個新的事務,如果目前存在事務,則把目前事務挂起。
 
3. TransactionDefinition.PROPAGATION_SUPPORTS:
   如果目前存在事務,則加入該事務;如果目前沒有事務,則以非事務的方式繼續運作。
 
4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
   以非事務方式運作,如果目前存在事務,則把目前事務挂起。
 
5. TransactionDefinition.PROPAGATION_NEVER:
   以非事務方式運作,如果目前存在事務,則抛出異常。
 
6. TransactionDefinition.PROPAGATION_MANDATORY:
   如果目前存在事務,則加入該事務;如果目前沒有事務,則抛出異常。
 
7. TransactionDefinition.PROPAGATION_NESTED:
   如果目前存在事務,則建立一個事務作為目前事務的嵌套事務來運作;
   如果目前沒有事務,則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。      

2.2、要注意的地方

  1. 預設事務:

Propagation propagation() default Propagation.REQUIRED

當然,可在注解中,根據需要,配置事務機制

  1. 聲明位置:

類上,所有方法預設事務,不推薦,影響了查詢效率(AOP)

方法上,該方法預設事務,但也要注意細節,包括修飾符,嵌套事務,異常處理等等,都會影響事務是否正确配置

  1. 修飾符
方法必須 public 修飾
  1. 異常處理
系統中的異常處理方式,很多,主要 return,throw ,try()catch(){}三種方式,隻有throw的異常,才會觸發事務
  1. 不同類嵌套事務

不同類之間的方法調用,如類A的方法a()調用類B的方法b(),這種情況事務是正常起作用的。隻要方法a()或b()配置了事務,運作中就會開啟事務,産生代理。

若兩個方法都配置了事務,兩個事務具體以何種方式傳播,取決于設定的事務傳播特性。

  1. 同類嵌套事務
@Service
    public class TestImpl implements TestService {
        @Autowired
        private Dao dao;

        public void insertUser(User user) {
            dao.insertUser(user);
            this.selectUser(user.getId());
        }

        @Transactional
        public String selectUser(int id) {
            throw new RuntimeException();
        }
    }      

這種同類中,事務方法的調用問題,預設情況下,隻有來自外部的方法調用才會被AOP代理捕獲,即是,類内部方法調用本類内部的其他方法并不會引起事務行為,即使被調用方法使用@Transactional注解進行修飾

解決方案:五種

方法1:将事務方法放到另一個類中(或者單獨開啟一層,取名“事務層”)進行調用,即符合了在對象之間調用的條件。

方法2:擷取本對象的代理對象,再進行調用.

  1. Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy=“true”/>
  2. 在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),擷取到xxxService的代理類,再調用事務方法,強行經過代理類,激活事務切面。

方法3:

很多時候,方法内調用又希望激活事務,是由于同一個方法既有DAO操作又有I/O等耗時操作,不想讓耗時的I/O造成事務的太長耗時(比如新增商品同時需要寫入庫存)。此時,可以将I/O做成異步操作(如加入線程池),而加入線程池的操作即便加入事務也不會導緻事務太長,問題可以迎刃而解。

方法4:手寫事務

自行學習

方法5:

接口内部方法調用的錯誤實作,可以用AspectJ 取代 Spring AOP 代理

2.2、常見異常

  • Connection is read-only,…操作隻讀事務異常
  • UnexpectedRollbackException: Transaction rolled … 方法嵌套事務異常
  • zzz
  • xxx

2.3、Spring中常用事務處理操作 / 場景,要注意的地方

2.3.1、 捕捉異常, 導緻不能復原

代碼示例,此處事務不會復原

@Override
    @Transactional
    public void CatchExceptionCanNotRollback() {
        try {
            extraAdDao.save(new ExtraAd("otii"));
            throw new RuntimeException();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }      

解決方案 :

  • 1、手動處理如下
@Override
    @Transactional
    public void CatchExceptionCanNotRollback() {

        try {
            extraAdDao.save(new ExtraAd("otii"));
            throw new RuntimeException();
        } catch (Exception ex) {
            ex.printStackTrace();
            // 手動标記復原
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }      
  • 2、不捕獲
@Override
    @Transactional
    public void RuntimeExceptionCanRollback() {
        extraAdDao.save(new ExtraAd("otii"));
        throw new RuntimeException();
    }      

2.3.2、 自定義異常,導緻事務不復原

代碼示例,無法復原

@Override
    @Transactional
    public void NotRuntimeExceptionCanNotRollback() throws CustomException {
        try {
            extraAdDao.save(new ExtraAd("otii"));
            throw new RuntimeException();
        } catch (Exception ex) {
            throw new CustomException(ex.getMessage());
        }
    }      

解決方案 :

  • 1、異常轉換
@Override
    @Transactional(rollbackFor = {CustomException.class})
    public void AssignExceptionCanRollback() throws CustomException {
        try {
            extraAdDao.save(new ExtraAd("otii"));
            throw new RuntimeException();
        } catch (Exception ex) {
            throw new CustomException(ex.getMessage());
        }
    }      

注意:這樣 指定也是可以的

@Override
    @Transactional(rollbackFor = {Exception.class})
    public void AssignExceptionCanRollback() throws CustomException {
        try {
            extraAdDao.save(new ExtraAd("otii"));
            throw new RuntimeException();
        } catch (Exception ex) {
            throw new CustomException(ex.getMessage());
        }
    }      

2.3.3、 嵌套方法

代碼示例,此處事務 不復原

public void oneSaveMethod() {
        extraAdDao.save(new ExtraAd("otii"));
    }

    @Override
    @Transactional
    public void RollbackOnlyCanRollback() throws Exception {
        oneSaveMethod();
        throw new Exception("");
    }      

解決方案:

  • 1、事務復原 - 異常指定
public void oneSaveMethod() {
        extraAdDao.save(new ExtraAd("otii"));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void RollbackOnlyCanRollback() throws Exception {
        oneSaveMethod();
        throw new Exception("");
    }      
  • 2、配置事務傳播機制
public void oneSaveMethod() {
        extraAdDao.save(new ExtraAd("otii"));
    }
    
    @Override
    @Transactional(propagation = Propagation.MANDATORY)
    public void RollbackOnlyCanRollback() throws CustomException {
        oneSaveMethod();
        throw new CustomException("");
    }      

2.3.4、 同一個類中, 一個不标注事務的方法去調用 transactional 的方法, 事務會失效

代碼示例,如下,事務不復原

@Override
    public void NonTransactionalCanNotRollback() {
        anotherOneSaveMethod();
    }
    
    @Transactional
    public void anotherOneSaveMethod() {
        extraAdDao.save(new ExtraAd("otii"));
        throw new RuntimeException();
    }      

這裡會有奇怪的現象,就是 會莫名 commit

解決方案 :

  • 1、調用事務方法時,加上 @Transactional 注解,即可解決

三、Mysql 事務

待記錄

四、Oracle 事務

待記錄

五、Nosql 事務

六、分布式事務

6.1、出現原因

6.2、JTA

JTA(Java Transaction API)提供了跨資料庫連接配接(或其他JTA資源)的事務管理能力。JTA事務管理則由JTA容器實作,J2ee架構中事務管理器與應用程式,資料總管,以及應用伺服器之間的事務通訊。

還有

七、總結