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>
優缺點
  • 缺點:理解難,配置復雜

  • 優點:代碼和事務分開。控制事務源代碼不用修改。

    ​ 能快速的了解和掌握項目的全部事務。適合大型項目。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM