1. 程式人生 > 其它 >spring事務詳解(基於註解和宣告的兩種實現方式)

spring事務詳解(基於註解和宣告的兩種實現方式)

Spring事務( Transaction )

事務的概念

事務是一些sql語句的集合,作為一個整體執行,一起成功或者一起失敗。

使用事務的時機

一個操作需要多天sql語句一起完成才能成功

程式中事務在哪裡說明

加在業務類的方法上面(public方法上面),表示業務方法執行時,需要事務的支援。

不同的事務管理器

不同的資料庫訪問技術,處理事務是不同的

  1. 使用jdbc 訪問資料庫,事務處理

    public void updateAccount(){
    	Connection con = .....;
    	con.setAutoCommit(false);
    	state.insert();
    	state.update();
    	state.commit();
    	con.setAutoCommit(true);
    }
    
  2. MyBatis執行資料庫,處理事務

    public void updateAccount(){
    	SqlSession sqlSession = SqlSessionFactory.openSession(false);
    	try{
    	 sqlSession.insert(...);
             sqlSession.update(...);
             sqlSession.commit();
    	}catch(Exception e){
    		sqlSession.rollback();
    	}
    }
    

spring統一管理事務,把不同的資料庫訪問技術的事務處理統一起來

使用spring的事務管理器,管理不同資料庫訪問技術的事務處理。開發人員只需要掌握spring的事務處理一個方案,就可以實現使用不同資料庫訪問技術的事務管理。

儘管事務面向的是spring,有spring管理事務,做事務提交和回滾。

spring事務管理器

spring框架使用事務管理器物件,管理所有的事務。

事務管理器介面: PlatFormTransactionManager

​ 作用 :定義了事務的操作,主要是commit() , rollback()

事務管理器有很多的實現類:一種資料庫訪問計數有一個實現類。由實現類具體完成事務的提交,回滾。

這意味著:JDBC或者MyBatis訪問資料庫有自己的事務管理實現類:DataSourceTransactionManager

​ hibernate框架,他的事務管理器實現類:HibernateTransactionManager

事務管理器的工作方式

spring集中統一管理事務,分配管理事務給具體的事務管理器物件。

事務提交和回滾的時機

當業務正常執行的時候,沒有異常,事務是提交的。如果業務程式碼中出現了執行時異常,事務會發生回滾。

異常的分類:

  • Error : 錯誤,回滾事務
  • Exception:
    • 執行時異常:RuntimeException和他的子類都是執行時異常,在程式執行過程中丟擲的異常
    • 受查異常:在編寫程式碼時需要處理的異常(編譯都無法通過),會提交事務。

方法丟擲執行時異常事務回滾,其他的情況都是執行事務。

spring事務管理的實現方式:AOP中的環繞通知

環繞通知:在目標方法的前後都可以增加功能,不需要修改程式碼。

// spring給業務方法在執行時,增加事務的切面功能(一下為大致的實現步驟,不是真正的執行程式碼)
@Around("execution(* com.wang.*.*(..))")
public Object myAround(ProceedingJoinPoint pjp){
    try{
        PlatformTransactionManager.beginTransaction(); // 使用spring的事務管理器,開啟事務
        pjp.proceed(); // 執行目標方法
        PlatformTransactionManager.commit(); // 業務方法正常執行,事務提交
    }catch(Exception e){
        PlatformTransactionManager.roolback(); // 業務方法非正常執行,事務回滾
    }
}

事務定義介面(TransactionDefinition )

事務定義介面TransactionDefinition中定義了事務描述相關的三類常量:事務隔離級別事務傳播行為事務預設超時時限,及他們的操作。

給業務方法說明事務的屬性。

隔離級別

定義

控制事務之間的影響速度

具體值
  • DEFAULT:採用DB預設的事務隔離級別。Mysql的預設為REPEATABLE_READOracle預設為READ_COMMITTED
  • READ_UNCOMMITTED:讀未提交。未解決任何併發問題
  • READ_COMMITTER:讀已提交。解決髒讀,存在不可重複讀和幻讀
  • REPETABLE_READ:可重複讀。解決髒讀,不可重複讀,存在幻讀
  • SERIALIZABLE:序列化。不存在併發問題。

事務超時時間

以秒為單位,整數值,預設為-1

超時時間:表示一個業務方法最長的執行時間,到達時間沒有執行完畢,spring會回滾事務。

傳播行為

傳播行為有7個值

定義:業務方法在呼叫的時候,事務在方法之間的傳遞和使用。

使用傳播行為,表示方法有無事務。

  • PROPAGATION_REQUIRED

  • PROPAGATION_REQUIRES_NEW

  • PROPAGATION_SUPPORTS

    以上三個需要掌握

  • PROPAGATION_MANDATORY

  • PROPAGATION_NESTED

  • PROPAGATION_NEVER

  • PROPAGATION_NOT_SUPPORTED

  1. PROPAGATION_REQUIRED:spring預設傳播行為,方法在呼叫的時候,如果存在事務就是用當前的事務,如果沒有事務,就新建一個事務,方法在新事務中執行。
  2. PROPAGATION_SUPPORTS: 方法有事務可以正常執行,沒有事務也可以正常執行。(查詢操作)
  3. PROPAGATION_REQUIRES_NEW:方法需要一個新事務。如果呼叫方法的時候,存在一個事務,則原來的事務暫停,知道新事務執行完畢。如果方法呼叫的時候,沒有事務,則新建一個事務,在新事務中執行程式碼。

spring框架使用自己的註解@Transactional控制事務

@Transactional註解,使用註解的屬性控制事務(隔離級別,傳播行為,超時)

​ 其中的屬性:

  • propagation:事務的傳播行為,他使用的Propagation類的列舉值,例如:Propagation.REQUIRED;
  • isolation:表示隔離級別,使用Isolation類的列舉值,表示隔離級別,預設是:Ioslation.DEFAULT;
  • readOnly:boolean型別的值,表示資料庫操作是不是隻讀的,預設為false
  • timeout:事務超時,預設是-1,整數值,單位是秒,例如:timeout=20;
  • rollbackFor:表示回滾的異常類列表,他的值是一個數組,每個值是異常型別的class。
  • rollbackForClassName:表示回滾的異常類列表,是String型別的值
  • noRollbackFor:不需要回滾的異常類列表,是class型別的
  • noRollbackForClassName:不需要回滾的異常類列表,是String型別的值

該註解放置的位置:

  1. 在業務方法上面,實在public方法上面(大多數)
  2. 在類的上面(幾乎見不到)

註解的使用步驟

  1. 在spring配置檔案中,宣告事務的內容;

    宣告事務管理器,說明使用哪個事務管理器物件;

    宣告使用註解管理事務,開啟註解驅動

  2. 在類的原始碼中,加入@Transactional

事務的控制模式:

  1. 程式設計式,在程式碼中程式設計控制事務
  2. 宣告式事務,不用編碼
步驟一:
<!--宣告事務管理器(連線到資料庫,做事務提交,事務回滾)-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
    <!--指定資料來源-->
    <property name="dataSource" ref="myDataSource"/>
</bean>

<!--開啟事務註解驅動-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--最後匯入的包是以tx結尾-->
步驟二:

在具體的方法上加上@Transactional註解

	@Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            timeout = 20,
            rollbackFor = {NullPointerException.class}
    )
    @Override
    public void buy(Integer goodId, Integer amount) throws Exception {
        System.out.println("buy方法的執行");
        Sale sale = new Sale();
        sale.setGid(goodId);
        sale.setNums(amount);
        // 生成銷售記錄
        saleDao.insertSale(sale);
        // 查詢商品
        Good good = goodDao.selectById(goodId);
        if (good == null){
            throw new Exception("商品不存在");
        }else if (good.getAmount() < amount){
            throw new Exception("庫存不足");
        }
        // 更新庫存
        Good good1 = new Good();
        good1.setId(goodId);
        good1.setAmount(amount);
        goodDao.updateGood(good1);
        System.out.println("buy方法的完成");
    }
rollbackFor說明
  1. 框架首先檢查方法丟擲的異常是不是在rollbackFor陣列中,如果在一定會發生回滾;
  2. 如果方法丟擲的異常不在rollbackFor陣列中,框架會繼續檢查丟擲的異常是不是RuntimrException,如果是RuntimeException,一定會發生回滾。

使用rollbackFor可以實現發生受檢查異常時讓其發生回滾。

註解的使用特點

直接使用@Transactional會使用預設值

  • spring框架自己提供的事務控制
  • 適合中小型專案,註解都放置在了原始碼中,不利於優化
  • 使用方便效率高

使用Aspectj框架在spring配置檔案中,宣告事務控制

使用aspectj的aop,宣告事務控制叫做宣告式事務

使用步驟

  1. 加入spring-aspectj的依賴
  2. spring的配置檔案宣告事務屬性
    • 宣告事務管理器
    • 宣告業務方法需要的事務屬性
    • 宣告切入點表示式
步驟

基本上是模板化的方法

<!--宣告式事務-->
<!--1.宣告事務管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager2">
    <property name="dataSource" ref="myDataSource"/>
</bean>

<!--2.宣告業務方法的事務屬性(隔離級別,傳播行為,超時)
            id:給業務方法配置事務段程式碼起個名字,唯一值
            transaction-manager:事務管理器的id
            -->
<tx:advice id="serviceAdvice" transaction-manager="transactionManager2">
    <!--給具體的業務方法增加事務的說明-->
    <tx:attributes>
        <!-- 給具體的業務方法,說明他需要的事務屬性
                name:業務方法名稱,配置name的值 1.業務方法的名稱(類中的某一個方法) 2.帶有部分萬用字元的方法名稱 3.使用*
                propagation:指定傳播行為
                isolation:隔離級別
                read-only:是否只讀,預設為false
                timeout:超時時間
                rollback-for:指定回滾的異常類列表,使用的異常類的全限定名稱(多個異常時用都好分割)
                -->
        <tx:method name="buy"
                   isolation="DEFAULT"
                   propagation="REQUIRED"
                   timeout="20"
                   rollback-for="java.lang.NullPointerException"
                   />

        <!--在業務方法有命名規則的時候,可以對一些方法使用事務-->
        <tx:method name="add*" propagation="REQUIRED" timeout="20" isolation="DEFAULT"/>
        <tx:method name="delete*" propagation="REQUIRED" timeout="20" isolation="DEFAULT"/>
        <!--以上方法以外的-->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!--通過上述方法的宣告,但是我們並不知道是哪個類的方法,讓上述宣告生效的方法:切入點表示式-->
<!--宣告切入點表示式:表示哪些包中的哪些類的方法參與事務-->
<aop:config>
    <!--宣告切入點表示式
          expression:切入點表示式,表示哪些類中的哪些方法要參與事務
            id:切入點表示式的名稱,唯一值
            -->
    <aop:pointcut id="servicePointCut" expression="execution(* *..service..*.*(..))"/>
    <!-- 關聯切入點表示式和事務的通知-->
    <aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointCut"/>
</aop:config>
優缺點
  • 缺點:理解難,配置複雜

  • 優點:程式碼和事務分開。控制事務原始碼不用修改。

    ​ 能快速的瞭解和掌握專案的全部事務。適合大型專案。