Spring 5 中文解析數據存儲篇-@Transactional使用

Spring核心篇章:html

Spring 5 中文解析之核心篇-IoC容器java

Spring 5 中文解析核心篇-IoC容器之依賴關係react

Spring 5 中文解析核心篇-IoC容器之Bean做用域git

Spring 5 中文解析核心篇-IoC容器之自定義Bean性質web

Spring 5 中文解析核心篇-IoC容器之BeanDefinition繼承與容器拓展點spring

Spring 5 中文解析核心篇-IoC容器之基於註解的容器配置編程

Spring 5 中文解析核心篇-IoC容器之類路徑掃描和組件管理api

Spring 5 中文解析核心篇-IoC容器之JSR330標準註解數組

Spring 5 中文解析核心篇-IoC容器之基於Java容器配置微信

Spring 5 中文解析核心篇-IoC容器之Environment抽象

Spring 5 中文解析核心篇-IoC容器之ApplicationContext與BeanFactory

Spring 5 中文解析核心篇-IoC容器之Resources

Spring 5 中文解析核心篇-IoC容器之數據校驗、數據綁定和類型轉換

Spring 5 中文解析核心篇-IoC容器之SpEL表達式

Spring 5 中文解析核心篇-IoC容器之AOP編程(上)")

Spring 5 中文解析核心篇-IoC容器之AOP編程(下)")

Spring 5 中文解析核心篇-IoC容器之Spring AOP API

Spring測試篇章:

Spring 5 中文解析測試篇-Spring測試

Spring 5 中文解析核心篇-集成測試之概要和集成測試註解

Spring 5 中文解析核心篇-集成測試之TestContext(上)")

Spring 5 中文解析核心篇-集成測試之TestContext(中)")

Spring 5 中文解析測試篇-集成測試之TestContext(下)")

Spring 5 中文解析測試篇-Spring MVC測試框架

Spring 5 中文解析測試篇-WebTestClient

Spring存儲篇章:

Spring 5 中文解析數據存儲篇-Spring框架的事物支持模型的優點

[Spring 5 中文解析數據存儲篇-事務同步和聲明式事物管理
](https://mp.weixin.qq.com/s?__...

[Spring 5 中文解析數據存儲篇-@Transactional使用
](https://mp.weixin.qq.com/s?__...

完整電子書地址

除了基於XML的聲明式方法進行事務配置外,還可使用基於註解的方法。直接在Java源代碼中聲明事務語義會使聲明更加接近受影響的代碼。不存在過多耦合的風險,由於本來打算以事務方式使用的代碼幾乎老是以這種方式部署。

還支持使用標準的 javax.transaction.Transactional註解來替代Spring本身的註解。請參閱 JTA 1.2文檔以獲取更多詳細信息。

使用@Transactional註解提供的易用性將經過一個示例獲得最好的說明,下面的示例對此進行了說明。考慮如下類定義:

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName) {
        // ...
    }

    Foo getFoo(String fooName, String barName) {
        // ...
    }

    void insertFoo(Foo foo) {
        // ...
    }

    void updateFoo(Foo foo) {
        // ...
    }
}

在上面的類級別使用,註解表示聲明類(及其子類)的全部方法的默認值。另外,每種方法均可以單獨註解。注意,類級別的註解不適用於類層次結構中的祖先類。在這種狀況下,須要在本地從新聲明方法,以參與子類級別的註解。

當將一個以上的POJO類在Spring上下文中定義爲bean時,能夠經過@Configuration類中的@EnableTransactionManagement註解使bean實例具備事務性。有關完整的詳細信息,請參見javadoc

在XML配置中,<tx:annotation-driven/>標籤提供了相似的便利操做:

<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- this is the service object that we want to make transactional -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="txManager"/><!-- a TransactionManager is still required --> //1

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- (this dependency is defined somewhere else) -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- other <bean/> definitions here -->

</beans>
  1. 使bean實例具備事務性的行爲
若是要鏈接的 TransactionManager的bean名稱具備名稱 transactionManager,則能夠在 <tx:annotation-driven/>標籤中省略 transaction-manager屬性。若是要依賴注入的 TransactionManager bean具備其餘名稱,則必須使用 transaction-manager屬性,如上例所示。

相對於命令式編程,響應式事務方法使用響應式返回類型,以下清單所示:

// the reactive service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Publisher<Foo> getFoo(String fooName) {
        // ...
    }

    Mono<Foo> getFoo(String fooName, String barName) {
        // ...
    }

    Mono<Void> insertFoo(Foo foo) {
        // ...
    }

    Mono<Void> updateFoo(Foo foo) {
        // ...
    }
}

請注意,對於返回的Publisher,在響應流取消信號方面有一些特殊注意事項。有關更多詳細信息,請參見「使用TransactionOperator」下的「取消信號」部分。

方法可見性和@Transactional

使用代理時,應僅將@Transactional註解應用於具備公共可見性的方法。若是使用@Transactional註解對protectedprivate或程序包可見的方法進行註解,則不會引起任何錯誤,可是<u>帶註解的方法不會顯示已配置的事務設置</u>。若是須要註解非公共方法,請考慮使用AspectJ(稍後描述)。

你能夠將@Transactional註解應用於接口定義、接口上的方法、類定義或類上的公共方法。可是,僅@Transactional註解的存在不足以激活事務行爲。 @Transactional註解僅僅是元數據,能夠被某些支持@Transactional的運行時基礎結構使用,而且可使用元數據來配置具備事務行爲的適當Bean。在前面的示例中,<tx:annotation-driven/>元素打開事務行爲。

Spring團隊推薦僅使用 @Transactional註解對具體類(以及具體類的方法)進行註解,而不是對接口進行註解。你固然能夠在接口(或接口方法)上放置 @Transactional註解,但這僅在你使用基於接口的代理時才能夠達到預期。Java註解不能從接口繼承的事實意味着,若是你使用基於類的代理( proxy-target-class="true")或基於編織的切面( mode =「aspectj」),則事務設置不會由代理和編織基礎結構識別,而且該對象未包裝在事務代理中。

在代理模式(默認)下,僅攔截經過代理傳入的外部方法調用。這意味着即便調用的方法標記有@Transactional,自調用(其實是目標對象中的方法調用目標對象的另外一種方法)也不會在運行時使用實際事務(譯者:調用實例自身的方法就是自調用)。另外,必須徹底初始化代理才能提供預期的行爲,所以你不該在初始化代碼(即@PostConstruct)中依賴此功能。

若是指望自調用也與事務包裝在一塊兒,請考慮使用AspectJ模式(請參見下表的mode屬性)。在這種狀況下,首先沒有代理。而是編織目標類(即,修改其字節碼)以將@Transactional轉換爲任何方法上的運行時行爲。

XML屬性 註解屬性 默認值 描述
transaction-manager N/A (查看 Transaction-ManagementConfigurer-javadoc) transactionManager 要使用的事務管理器的名稱。如上例所示,僅當事務管理器的名稱不是transactionManager時才須要。
mode mode proxy 默認模式(代理)經過使用Spring的AOP框架來處理帶註解的bean(遵循代理語義,如前所述,僅適用於經過代理傳入的方法調用)。替代模式(aspectj)則將受影響的類與Spring的AspectJ事務切面進行編織,修改目標類字節碼以應用於任何類型的方法調用。AspectJ編織須要在類路徑中使用spring-aspects.jar並啓用加載時編織(或編譯時編織)。(有關如何設置加載時編織的詳細信息,請參見Spring配置。)
proxy-target-class proxyTargetClass false 僅適用於代理模式。控制爲使用@Transactional註解註釋的類建立哪一種類型的事務代理。若是proxy-target-class屬性設置爲true,則將建立基於類的代理。若是proxy-target-classfalse或省略了屬性,則將建立基於標準JDK接口的代理。(有關不一樣代理類型的詳細檢查,請參見代理機制。)
order order Ordered.LOWEST_PRECEDENCE 定義應用於帶@Transactional註解的bean的事務通知的順序。(有關AOP通知排序相關規則的更多信息,請參見通知順序。)沒有指定的順序意味着AOP子系統肯定通知的順序。
處理 @Transactional註解的默認通知模式是代理,它僅容許經過代理攔截調用。同一類內的本地調用沒法以這種方式被攔截(自調用)。對於更高級的攔截模式,請考慮結合編譯時或加載時編織切換到 Aspectj模式。

proxy-target-class屬性控制爲使用@Transactional註解註釋的類建立哪一種類型的事務代理。若是proxy-target-class設置爲true,則將建立基於類的代理。若是proxy-target-classfalse或省略了屬性,則將建立基於標準JDK接口的代理。(有關不一樣代理類型的討論,請參見core.html。)

@EnableTransactionManagement<tx:annotation-driven/>僅在定義它們的相同應用程序上下文中的bean上查找@Transactional。這意味着,若是將註解驅動的配置放在DispatcherServletWebApplicationContext中,它將僅在控制器而不是服務中檢查@Transactional bean。有關更多信息,請參見MVC

在評估方法的事務設置時,最派生的位置優先(譯者:範圍最小的優先級越高,例如:類和方法配置時方法優先級更高)。在下面的示例中,DefaultFooService類在類級別使用只讀事務的設置進行註解,可是同一類中updateFoo(Foo)方法上的@Transactional註解優先於定義的事務設置在類級別上。

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
        // ...
    }

    // these settings have precedence for this method
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateFoo(Foo foo) {
        // ...
    }
}

@Transactional設置

@Transactional註解是元數據,它指定接口、類或方法必須具備事務語義(例如,在調用此方法時啓動一個全新的只讀事務、暫停任何現有事務)。默認的@Transactional設置以下:

  • 事物傳播設置爲PROPAGATION_REQUIRED
  • 事物隔離級別爲ISOLATION_DEFAULT
  • 事務是讀寫的。
  • 事務超時默認爲基礎事務系統的默認超時,若是不支持超時,則默認爲無。
  • 任何RuntimeException都會觸發回滾,而任何檢測的Exception都不會觸發。

你能夠更改這些默認設置。下表總結了@Transactional註解的各類屬性:

屬性 類型 描述
value String 可選的限定符,指定要使用的事務管理器。
propagation enum: Propagation 可選的傳播設置。
isolation enum: Isolation 可選的隔離級別。僅適用於REQUIREDREQUIRES_NEW的傳播值。
timeout int (以秒爲單位) 可選的事務超時。僅適用於REQUIREDREQUIRES_NEW的傳播值。
readOnly boolean 讀寫與只讀事務。僅適用於REQUIREDREQUIRES_NEW的值。
rollbackFor Class對象數組,必須從Throwable派生。 必須引發回滾的異常類的可選數組。
rollbackForClassName 類名數組。這些類必須從Throwable派生。 必須引發回滾的異常類名稱的可選數組。
noRollbackFor Class對象數組,必須從Throwable派生。 不能致使回滾的異常類的可選數組。
noRollbackForClassName 字符串類名稱的數組,必須從Throwable派生。 不能引發回滾的異常類名稱的可選數組。

當前,你沒法對事務名稱進行顯式控制,其中「名稱」是指顯示在事務監視器(若是適用)(例如,WebLogic的事務監視器)和日誌輸出中的事務名稱。對於聲明式事務,事務名稱始終是全限定類名稱+'.' +通知類的方法名稱。例如,若是BusinessService類的handlePayment(..)方法啓動了事務,則事務的名稱將爲:com.example.BusinessService.handlePayment

具備@Transactional的多個事務管理器

大多數Spring應用程序僅須要一個事務管理器,可是在某些狀況下,你可能須要在一個應用程序中使用多個獨立的事務管理器。你可使用@Transactional註解的valuetransactionManager屬性來選擇指定要使用的TransactionManager。這能夠是事務管理器bean的bean名稱或限定符值。例如,使用限定符表示法,能夠在應用程序上下文中將如下Java代碼與如下事務管理器bean聲明進行組合:

public class TransactionalService {

    @Transactional("order")
    public void setSomething(String name) { ... }

    @Transactional("account")
    public void doSomething() { ... }

    @Transactional("reactive-account")
    public Mono<Void> doSomethingReactive() { ... }
}

如下清單顯示了bean聲明:

<tx:annotation-driven/>

    <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        ...
        <qualifier value="order"/>
    </bean>

    <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        ...
        <qualifier value="account"/>
    </bean>

    <bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
        ...
        <qualifier value="reactive-account"/>
    </bean>
</tx:annotation-driven>

在這種狀況下,TransactionalService上的各個方法在單獨的事務管理器下運行,並根據orderaccountreactive-account限定符進行區分。若是未找到特別限定的TransactionManager bean,則仍使用默認的<tx:annotation-driven>目標bean名稱transactionManager

參考代碼: org.liyong.dataaccess.starter.QualifierAnnotationTransactionManagerIocContainer

自定義組合的註解

若是你發如今許多不一樣的方法上將@Transactional重複使用相同的屬性,則Spring的元註解支持可以讓你爲特定用例定義自定義的註解。例如,考慮如下註解定義:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("order")
public @interface OrderTx {
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("account")
public @interface AccountTx {
}

前面的註解使咱們能夠按照上一節的內容編寫示例,以下所示:

public class TransactionalService {

    @OrderTx
    public void setSomething(String name) {
        // ...
    }

    @AccountTx
    public void doSomething() {
        // ...
    }
}

在前面的示例中,咱們使用了語法來定義事務管理器限定符,可是咱們還能夠包括傳播行爲,回滾規則、超時和其餘功能。

參考代碼: org.liyong.dataaccess.starter.CustomAnnotationTransactionManagerIocContainer

做者

我的從事金融行業,就任過易極付、思建科技、某網約車平臺等重慶一流技術團隊,目前就任於某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大數據、數據存儲、自動化集成和部署、分佈式微服務、響應式編程、人工智能等領域。同時也熱衷於技術分享創立公衆號和博客站點對知識體系進行分享。關注公衆號: 青年IT男 獲取最新技術文章推送!

博客地址: http://youngitman.tech

CSDN: https://blog.csdn.net/liyong1...

微信公衆號:

技術交流羣:

相關文章
相關標籤/搜索