Spring源碼剖析8:Spring事務(wù)概述

原文出處: 張開(kāi)濤

數(shù)據(jù)庫(kù)事務(wù)概述

事務(wù)首先是一系列操作組成的工作單元,該工作單元內(nèi)的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,這就是事務(wù)。

事務(wù)必需滿(mǎn)足ACID(原子性、一致性、隔離性和持久性)特性,缺一不可:

  • 原子性(Atomicity):即事務(wù)是不可分割的最小工作單元,事務(wù)內(nèi)的操作要么全做,要么全不做;
  • 一致性(Consistency):在事務(wù)執(zhí)行前數(shù)據(jù)庫(kù)的數(shù)據(jù)處于正確的狀態(tài),而事務(wù)執(zhí)行完成后數(shù)據(jù)庫(kù)的數(shù)據(jù)還是處于正確的狀態(tài),即數(shù)據(jù)完整性約束沒(méi)有被破壞;如銀行轉(zhuǎn)帳,A轉(zhuǎn)帳給B,必須保證A的錢(qián)一定轉(zhuǎn)給B,一定不會(huì)出現(xiàn)A的錢(qián)轉(zhuǎn)了但B沒(méi)收到,否則數(shù)據(jù)庫(kù)的數(shù)據(jù)就處于不一致(不正確)的狀態(tài)。
  • 隔離性(Isolation):并發(fā)事務(wù)執(zhí)行之間無(wú)影響,在一個(gè)事務(wù)內(nèi)部的操作對(duì)其他事務(wù)是不產(chǎn)生影響,這需要事務(wù)隔離級(jí)別來(lái)指定隔離性;
  • 持久性(Durability):事務(wù)一旦執(zhí)行成功,它對(duì)數(shù)據(jù)庫(kù)的數(shù)據(jù)的改變必須是永久的,不會(huì)因比如遇到系統(tǒng)故障或斷電造成數(shù)據(jù)不一致或丟失。

在實(shí)際項(xiàng)目開(kāi)發(fā)中數(shù)據(jù)庫(kù)操作一般都是并發(fā)執(zhí)行的,即有多個(gè)事務(wù)并發(fā)執(zhí)行,并發(fā)執(zhí)行就可能遇到問(wèn)題,目前常見(jiàn)的問(wèn)題如下:

  • 丟失更新:兩個(gè)事務(wù)同時(shí)更新一行數(shù)據(jù),最后一個(gè)事務(wù)的更新會(huì)覆蓋掉第一個(gè)事務(wù)的更新,從而導(dǎo)致第一個(gè)事務(wù)更新的數(shù)據(jù)丟失,這是由于沒(méi)有加鎖造成的;
  • 臟讀:一個(gè)事務(wù)看到了另一個(gè)事務(wù)未提交的更新數(shù)據(jù);
  • 不可重復(fù)讀:在同一事務(wù)中,多次讀取同一數(shù)據(jù)卻返回不同的結(jié)果;也就是有其他事務(wù)更改了這些數(shù)據(jù);
  • 幻讀:一個(gè)事務(wù)在執(zhí)行過(guò)程中讀取到了另一個(gè)事務(wù)已提交的插入數(shù)據(jù);即在第一個(gè)事務(wù)開(kāi)始時(shí)讀取到一批數(shù)據(jù),但此后另一個(gè)事務(wù)又插入了新數(shù)據(jù)并提交,此時(shí)第一個(gè)事務(wù)又讀取這批數(shù)據(jù)但發(fā)現(xiàn)多了一條,即好像發(fā)生幻覺(jué)一樣。

為了解決這些并發(fā)問(wèn)題,需要通過(guò)數(shù)據(jù)庫(kù)隔離級(jí)別來(lái)解決,在標(biāo)準(zhǔn)SQL規(guī)范中定義了四種隔離級(jí)別:

  • 未提交讀(Read Uncommitted):最低隔離級(jí)別,一個(gè)事務(wù)能讀取到別的事務(wù)未提交的更新數(shù)據(jù),很不安全,可能出現(xiàn)丟失更新、臟讀、不可重復(fù)讀、幻讀;
  • 提交讀(Read Committed):一個(gè)事務(wù)能讀取到別的事務(wù)提交的更新數(shù)據(jù),不能看到未提交的更新數(shù)據(jù),不可能可能出現(xiàn)丟失更新、臟讀,但可能出現(xiàn)不可重復(fù)讀、幻讀;
  • 可重復(fù)讀(Repeatable Read):保證同一事務(wù)中先后執(zhí)行的多次查詢(xún)將返回同一結(jié)果,不受其他事務(wù)影響,可能可能出現(xiàn)丟失更新、臟讀、不可重復(fù)讀,但可能出現(xiàn)幻讀;
  • 序列化(Serializable):最高隔離級(jí)別,不允許事務(wù)并發(fā)執(zhí)行,而必須串行化執(zhí)行,最安全,不可能出現(xiàn)更新、臟讀、不可重復(fù)讀、幻讀。

隔離級(jí)別越高,數(shù)據(jù)庫(kù)事務(wù)并發(fā)執(zhí)行性能越差,能處理的操作越少。因此在實(shí)際項(xiàng)目開(kāi)發(fā)中為了考慮并發(fā)性能一般使用提交讀隔離級(jí)別,它能避免丟失更新和臟讀,盡管不可重復(fù)讀和幻讀不能避免,但可以在可能出現(xiàn)的場(chǎng)合使用悲觀鎖或樂(lè)觀鎖來(lái)解決這些問(wèn)題。

事務(wù)類(lèi)型

數(shù)據(jù)庫(kù)事務(wù)類(lèi)型有本地事務(wù)和分布式事務(wù):

  • 本地事務(wù):就是普通事務(wù),能保證單臺(tái)數(shù)據(jù)庫(kù)上的操作的ACID,被限定在一臺(tái)數(shù)據(jù)庫(kù)上;
  • 分布式事務(wù):涉及兩個(gè)或多個(gè)數(shù)據(jù)庫(kù)源的事務(wù),即跨越多臺(tái)同類(lèi)或異類(lèi)數(shù)據(jù)庫(kù)的事務(wù)(由每臺(tái)數(shù)據(jù)庫(kù)的本地事務(wù)組成的),分布式事務(wù)旨在保證這些本地事務(wù)的所有操作的ACID,使事務(wù)可以跨越多臺(tái)數(shù)據(jù)庫(kù);

Java事務(wù)類(lèi)型有JDBC事務(wù)和JTA事務(wù):

  • JDBC事務(wù):就是數(shù)據(jù)庫(kù)事務(wù)類(lèi)型中的本地事務(wù),通過(guò)Connection對(duì)象的控制來(lái)管理事務(wù);
  • JTA事務(wù):JTA指Java事務(wù)API(Java Transaction API),是Java EE數(shù)據(jù)庫(kù)事務(wù)規(guī)范, JTA只提供了事務(wù)管理接口,由應(yīng)用程序服務(wù)器廠(chǎng)商(如WebSphere Application Server)提供實(shí)現(xiàn),JTA事務(wù)比JDBC更強(qiáng)大,支持分布式事務(wù)。

Java EE事務(wù)類(lèi)型有本地事務(wù)和全局事務(wù):

  • 本地事務(wù):使用JDBC編程實(shí)現(xiàn)事務(wù);
  • 全局事務(wù):由應(yīng)用程序服務(wù)器提供,使用JTA事務(wù);

按是否通過(guò)編程實(shí)現(xiàn)事務(wù)有聲明式事務(wù)和編程式事務(wù);

  • 聲明式事務(wù): 通過(guò)注解或XML配置文件指定事務(wù)信息;
  • 編程式事務(wù):通過(guò)編寫(xiě)代碼實(shí)現(xiàn)事務(wù)。

Spring提供的事務(wù)管理

Spring框架最核心功能之一就是事務(wù)管理,而且提供一致的事務(wù)管理抽象,這能幫助我們:

  • 提供一致的編程式事務(wù)管理API,不管使用Spring JDBC框架還是集成第三方框架使用該API進(jìn)行事務(wù)編程;
  • 無(wú)侵入式的聲明式事務(wù)支持。

Spring支持聲明式事務(wù)和編程式事務(wù)事務(wù)類(lèi)型。

spring事務(wù)特性

spring所有的事務(wù)管理策略類(lèi)都繼承自org.springframework.transaction.PlatformTransactionManager接口

其中TransactionDefinition接口定義以下特性:

事務(wù)隔離級(jí)別

隔離級(jí)別是指若干個(gè)并發(fā)的事務(wù)之間的隔離程度。TransactionDefinition 接口中定義了五個(gè)表示隔離級(jí)別的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:這是默認(rèn)值,表示使用底層數(shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別。對(duì)大部分?jǐn)?shù)據(jù)庫(kù)而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級(jí)別表示一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)修改但還沒(méi)有提交的數(shù)據(jù)。該級(jí)別不能防止臟讀,不可重復(fù)讀和幻讀,因此很少使用該隔離級(jí)別。比如PostgreSQL實(shí)際上并沒(méi)有此級(jí)別。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級(jí)別表示一個(gè)事務(wù)只能讀取另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)。該級(jí)別可以防止臟讀,這也是大多數(shù)情況下的推薦值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級(jí)別表示一個(gè)事務(wù)在整個(gè)過(guò)程中可以多次重復(fù)執(zhí)行某個(gè)查詢(xún),并且每次返回的記錄都相同。該級(jí)別可以防止臟讀和不可重復(fù)讀。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。

事務(wù)傳播行為

  所謂事務(wù)的傳播行為是指,如果在開(kāi)始當(dāng)前事務(wù)之前,一個(gè)事務(wù)上下文已經(jīng)存在,此時(shí)有若干選項(xiàng)可以指定一個(gè)事務(wù)性方法的執(zhí)行行為。在TransactionDefinition定義中包括了如下幾個(gè)表示傳播行為的常量:
  • TransactionDefinition.PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。這是默認(rèn)值。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則拋出異常。
  • TransactionDefinition.PROPAGATION_NESTED:如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行;如果當(dāng)前沒(méi)有事務(wù),則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED。

事務(wù)超時(shí)

  所謂事務(wù)超時(shí),就是指一個(gè)事務(wù)所允許執(zhí)行的最長(zhǎng)時(shí)間,如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)。在 TransactionDefinition 中以 int 的值來(lái)表示超時(shí)時(shí)間,其單位是秒。

默認(rèn)設(shè)置為底層事務(wù)系統(tǒng)的超時(shí)值,如果底層數(shù)據(jù)庫(kù)事務(wù)系統(tǒng)沒(méi)有設(shè)置超時(shí)值,那么就是none,沒(méi)有超時(shí)限制。

事務(wù)只讀屬性

  只讀事務(wù)用于客戶(hù)代碼只讀但不修改數(shù)據(jù)的情形,只讀事務(wù)用于特定情景下的優(yōu)化,比如使用Hibernate的時(shí)候。

默認(rèn)為讀寫(xiě)事務(wù)。

概述

Spring框架支持事務(wù)管理的核心是事務(wù)管理器抽象,對(duì)于不同的數(shù)據(jù)訪(fǎng)問(wèn)框架(如Hibernate)通過(guò)實(shí)現(xiàn)策略接口PlatformTransactionManager,從而能支持各種數(shù)據(jù)訪(fǎng)問(wèn)框架的事務(wù)管理,PlatformTransactionManager接口定義如下:

java代碼:

public interface PlatformTransactionManager {
       TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
       void commit(TransactionStatus status) throws TransactionException;
       void rollback(TransactionStatus status) throws TransactionException;
}
  • getTransaction():返回一個(gè)已經(jīng)激活的事務(wù)或創(chuàng)建一個(gè)新的事務(wù)(根據(jù)給定的TransactionDefinition類(lèi)型參數(shù)定義的事務(wù)屬性),返回的是TransactionStatus對(duì)象代表了當(dāng)前事務(wù)的狀態(tài),其中該方法拋出TransactionException(未檢查異常)表示事務(wù)由于某種原因失敗。
  • commit():用于提交TransactionStatus參數(shù)代表的事務(wù),具體語(yǔ)義請(qǐng)參考Spring Javadoc;
  • rollback():用于回滾TransactionStatus參數(shù)代表的事務(wù),具體語(yǔ)義請(qǐng)參考Spring Javadoc。

TransactionDefinition接口定義如下:

java代碼:

public interface TransactionDefinition {
       int getPropagationBehavior();
       int getIsolationLevel();
       int getTimeout();
       boolean isReadOnly();
       String getName();
}
  • getPropagationBehavior():返回定義的事務(wù)傳播行為;
  • getIsolationLevel():返回定義的事務(wù)隔離級(jí)別;
  • getTimeout():返回定義的事務(wù)超時(shí)時(shí)間;
  • isReadOnly():返回定義的事務(wù)是否是只讀的;
  • getName():返回定義的事務(wù)名字。

TransactionStatus接口定義如下:

java代碼:

public interface TransactionStatus extends SavepointManager {
       boolean isNewTransaction();
       boolean hasSavepoint();
       void setRollbackOnly();
       boolean isRollbackOnly();
       void flush();
       boolean isCompleted();
}
  • isNewTransaction():返回當(dāng)前事務(wù)狀態(tài)是否是新事務(wù);
  • hasSavepoint():返回當(dāng)前事務(wù)是否有保存點(diǎn);
  • setRollbackOnly():設(shè)置當(dāng)前事務(wù)應(yīng)該回滾;
  • isRollbackOnly(():返回當(dāng)前事務(wù)是否應(yīng)該回滾;
  • flush():用于刷新底層會(huì)話(huà)中的修改到數(shù)據(jù)庫(kù),一般用于刷新如Hibernate/JPA的會(huì)話(huà),可能對(duì)如JDBC類(lèi)型的事務(wù)無(wú)任何影響;
  • isCompleted():當(dāng)前事務(wù)否已經(jīng)完成。

內(nèi)置事務(wù)管理器實(shí)現(xiàn)

Spring提供了許多內(nèi)置事務(wù)管理器實(shí)現(xiàn):

  • DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,數(shù)據(jù)源事務(wù)管理器,提供對(duì)單個(gè)javax.sql.DataSource事務(wù)管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事務(wù)管理;
  • JdoTransactionManager:位于org.springframework.orm.jdo包中,提供對(duì)單個(gè)javax.jdo.PersistenceManagerFactory事務(wù)管理,用于集成JDO框架時(shí)的事務(wù)管理;
  • JpaTransactionManager:位于org.springframework.orm.jpa包中,提供對(duì)單個(gè)javax.persistence.EntityManagerFactory事務(wù)支持,用于集成JPA實(shí)現(xiàn)框架時(shí)的事務(wù)管理;
  • HibernateTransactionManager:位于org.springframework.orm.hibernate3包中,提供對(duì)單個(gè)org.hibernate.SessionFactory事務(wù)支持,用于集成Hibernate框架時(shí)的事務(wù)管理;該事務(wù)管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本;
  • JtaTransactionManager:位于org.springframework.transaction.jta包中,提供對(duì)分布式事務(wù)管理的支持,并將事務(wù)管理委托給Java EE應(yīng)用服務(wù)器事務(wù)管理器;
  • OC4JjtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對(duì)OC4J10.1.3+應(yīng)用服務(wù)器事務(wù)管理器的適配器,此適配器用于對(duì)應(yīng)用服務(wù)器提供的高級(jí)事務(wù)的支持;
  • WebSphereUowTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對(duì)WebSphere 6.0+應(yīng)用服務(wù)器事務(wù)管理器的適配器,此適配器用于對(duì)應(yīng)用服務(wù)器提供的高級(jí)事務(wù)的支持;
  • WebLogicJtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對(duì)WebLogic 8.1+應(yīng)用服務(wù)器事務(wù)管理器的適配器,此適配器用于對(duì)應(yīng)用服務(wù)器提供的高級(jí)事務(wù)的支持。

Spring不僅提供這些事務(wù)管理器,還提供對(duì)如JMS事務(wù)管理的管理器等,Spring提供一致的事務(wù)抽象如圖9-1所示。


圖9-1 Spring事務(wù)管理器

接下來(lái)讓我們學(xué)習(xí)一下如何在Spring配置文件中定義事務(wù)管理器:

一、聲明對(duì)本地事務(wù)的支持:

a)JDBC及iBATIS、MyBatis框架事務(wù)管理器

java代碼:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

通過(guò)dataSource屬性指定需要事務(wù)管理的單個(gè)javax.sql.DataSource對(duì)象。

b)Jdo事務(wù)管理器

java代碼:

<bean id="txManager" class="org.springframework.orm.jdo.JdoTransactionManager">
    <property name="persistenceManagerFactory" ref="persistenceManagerFactory"/>
</bean>

通過(guò)persistenceManagerFactory屬性指定需要事務(wù)管理的javax.jdo.PersistenceManagerFactory對(duì)象。

c)Jpa事務(wù)管理器

java代碼:

bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

通過(guò)entityManagerFactory屬性指定需要事務(wù)管理的javax.persistence.EntityManagerFactory對(duì)象。

還需要為entityManagerFactory對(duì)象指定jpaDialect屬性,該屬性所對(duì)應(yīng)的對(duì)象指定了如何獲取連接對(duì)象、開(kāi)啟事務(wù)、關(guān)閉事務(wù)等事務(wù)管理相關(guān)的行為。

java代碼:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        ……
        <property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>

d)Hibernate事務(wù)管理器

java代碼:

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

通過(guò)entityManagerFactory屬性指定需要事務(wù)管理的org.hibernate.SessionFactory對(duì)象。

聲明式事務(wù)

聲明式事務(wù)概述

從上節(jié)編程式實(shí)現(xiàn)事務(wù)管理可以深刻體會(huì)到編程式事務(wù)的痛苦,即使通過(guò)代理配置方式也是不小的工作量。

本節(jié)將介紹聲明式事務(wù)支持,使用該方式后最大的獲益是簡(jiǎn)單,事務(wù)管理不再是令人痛苦的,而且此方式屬于無(wú)侵入式,對(duì)業(yè)務(wù)邏輯實(shí)現(xiàn)無(wú)影響。

接下來(lái)先來(lái)看看聲明式事務(wù)如何實(shí)現(xiàn)吧。

聲明式實(shí)現(xiàn)事務(wù)管理

1、定義業(yè)務(wù)邏輯實(shí)現(xiàn),此處使用ConfigUserServiceImpl和ConfigAddressServiceImpl:

2、定義配置文件(chapter9/service/ applicationContext-service-declare.xml):

2.1、XML命名空間定義,定義用于事務(wù)支持的tx命名空間和AOP支持的aop命名空間:

java代碼:
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="
 
http://www.springframework.org/schema/beans
 
 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 
 
http://www.springframework.org/schema/tx
 
 
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
 
 
http://www.springframework.org/schema/aop
 
 
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

2.2、業(yè)務(wù)實(shí)現(xiàn)配置,非常簡(jiǎn)單,使用以前定義的非侵入式業(yè)務(wù)實(shí)現(xiàn):

java代碼:
<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">
    <property name="userDao" ref="userDao"/>
    <property name="addressService" ref="addressService"/>
</bean>
<bean id="addressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">
    <property name="addressDao" ref="addressDao"/>
</bean>

2.3、事務(wù)相關(guān)配置:

java代碼:
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
        <tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/>
    </tx:attributes>
</tx:advice>

java代碼:

<aop:config>
    <aop:pointcut id="serviceMethod" expression="execution(* cn..chapter9.service..*.*(..))"/>
    <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
<tx:advice>:事務(wù)通知定義,用于指定事務(wù)屬性,其中“transaction-manager”屬性指定事務(wù)管理器,并通過(guò)< tx:attributes >指定具體需要攔截的方法;
 <tx:method name=”save*”>:表示將攔截以save開(kāi)頭的方法,被攔截的方法將應(yīng)用配置的事務(wù)屬性:propagation=”REQUIRED”表示傳播行為是Required,isolation=”READ_COMMITTED”表示隔離級(jí)別是提交讀;
<tx:method name=”*”>:表示將攔截其他所有方法,被攔截的方法將應(yīng)用配置的事務(wù)屬性:propagation=”REQUIRED”表示傳播行為是Required,isolation=”READ_COMMITTED”表示隔離級(jí)別是提交讀,read-only=”true”表示事務(wù)只讀;
<aop:config>:AOP相關(guān)配置:
<aop:pointcut/>:切入點(diǎn)定義,定義名為”serviceMethod”的aspectj切入點(diǎn),切入點(diǎn)表達(dá)式為”execution(* cn..chapter9.service..*.*(..))”表示攔截cn包及子包下的chapter9. service包及子包下的任何類(lèi)的任何方法;
<aop:advisor>:Advisor定義,其中切入點(diǎn)為serviceMethod,通知為txAdvice。
從配置中可以看出,將對(duì)cn包及子包下的chapter9. service包及子包下的任何類(lèi)的任何方法應(yīng)用“txAdvice”通知指定的事務(wù)屬性。

3、修改測(cè)試方法并測(cè)試該配置方式是否好用:

將TransactionTest 類(lèi)的testServiceTransaction測(cè)試方法拷貝一份命名為testDeclareTransaction:

并在testDeclareTransaction測(cè)試方法內(nèi)將:

4、執(zhí)行測(cè)試,測(cè)試正常通過(guò),說(shuō)明該方式能正常工作,當(dāng)調(diào)用save方法時(shí)將匹配到事務(wù)通知中定義的“<tx:method name=”save”>”中指定的事務(wù)屬性,而調(diào)用countAll方法時(shí)將匹配到事務(wù)通知中定義的“<tx:method name=””>”中指定的事務(wù)屬性。

聲明式事務(wù)是如何實(shí)現(xiàn)事務(wù)管理的呢?還記不記得TransactionProxyFactoryBean實(shí)現(xiàn)配置式事務(wù)管理,配置式事務(wù)管理是通過(guò)代理方式實(shí)現(xiàn),而聲明式事務(wù)管理同樣是通過(guò)AOP代理方式實(shí)現(xiàn)。

聲明式事務(wù)通過(guò)AOP代理方式實(shí)現(xiàn)事務(wù)管理,利用環(huán)繞通知TransactionInterceptor實(shí)現(xiàn)事務(wù)的開(kāi)啟及關(guān)閉,而TransactionProxyFactoryBean內(nèi)部也是通過(guò)該環(huán)繞通知實(shí)現(xiàn)的,因此可以認(rèn)為是<tx:tags/>幫你定義了TransactionProxyFactoryBean,從而簡(jiǎn)化事務(wù)管理。

了解了實(shí)現(xiàn)方式后,接下來(lái)詳細(xì)學(xué)習(xí)一下配置吧:

9.4.4 <tx:advice/>配置詳解
聲明式事務(wù)管理通過(guò)配置<tx:advice/>來(lái)定義事務(wù)屬性,配置方式如下所示:

java代碼:
<tx:advice id="……" transaction-manager="……">
<tx:attributes>
        <tx:method name="……"
                           propagation=" REQUIRED"
                           isolation="READ_COMMITTED"
                           timeout="-1"
                           read-only="false"
                           no-rollback-for=""
                           rollback-for=""/>
        ……
    </tx:attributes>
</tx:advice>
<tx:advice>:id用于指定此通知的名字, transaction-manager用于指定事務(wù)管理器,默認(rèn)的事務(wù)管理器名字為“transactionManager”;
<tx:method>:用于定義事務(wù)屬性即相關(guān)聯(lián)的方法名;

name:定義與事務(wù)屬性相關(guān)聯(lián)的方法名,將對(duì)匹配的方法應(yīng)用定義的事務(wù)屬性,可以使用“”通配符來(lái)匹配一組或所有方法,如“save”將匹配以save開(kāi)頭的方法,而“*”將匹配所有方法;

propagation:事務(wù)傳播行為定義,默認(rèn)為“REQUIRED”,表示Required,其值可以通過(guò)TransactionDefinition的靜態(tài)傳播行為變量的“PROPAGATION_”后邊部分指定,如“TransactionDefinition.PROPAGATION_REQUIRED”可以使用“REQUIRED”指定;

isolation:事務(wù)隔離級(jí)別定義;默認(rèn)為“DEFAULT”,其值可以通過(guò)TransactionDefinition的靜態(tài)隔離級(jí)別變量的“ISOLATION_”后邊部分指定,如“TransactionDefinition. ISOLATION_DEFAULT”可以使用“DEFAULT”指定:

timeout:事務(wù)超時(shí)時(shí)間設(shè)置,單位為秒,默認(rèn)-1,表示事務(wù)超時(shí)將依賴(lài)于底層事務(wù)系統(tǒng);

read-only:事務(wù)只讀設(shè)置,默認(rèn)為false,表示不是只讀;

rollback-for:需要觸發(fā)回滾的異常定義,以“,”分割,默認(rèn)任何RuntimeException 將導(dǎo)致事務(wù)回滾,而任何Checked Exception 將不導(dǎo)致事務(wù)回滾;異常名字定義和TransactionProxyFactoryBean中含義一樣

no-rollback-for:不被觸發(fā)進(jìn)行回滾的 Exception(s);以“,”分割;異常名字定義和TransactionProxyFactoryBean中含義一樣;

記不記得在配置方式中為了解決“自我調(diào)用”而導(dǎo)致的不能設(shè)置正確的事務(wù)屬性問(wèn)題,使用“((IUserService)AopContext.currentProxy()).otherTransactionMethod()”方式解決,在聲明式事務(wù)要得到支持需要使用<aop:config expose-proxy=”true”>來(lái)開(kāi)啟。

9.4.5 多事務(wù)語(yǔ)義配置及最佳實(shí)踐
什么是多事務(wù)語(yǔ)義?說(shuō)白了就是為不同的Bean配置不同的事務(wù)屬性,因?yàn)槲覀冺?xiàng)目中不可能就幾個(gè)Bean,而可能很多,這可能需要為Bean分組,為不同組的Bean配置不同的事務(wù)語(yǔ)義。在Spring中,可以通過(guò)配置多切入點(diǎn)和多事務(wù)通知并通過(guò)不同方式組合使用即可。

   1、首先看下聲明式事務(wù)配置的最佳實(shí)踐吧:

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
           <tx:method name="save*" propagation="REQUIRED" />
           <tx:method name="add*" propagation="REQUIRED" />
           <tx:method name="create*" propagation="REQUIRED" />
           <tx:method name="insert*" propagation="REQUIRED" />
           <tx:method name="update*" propagation="REQUIRED" />
           <tx:method name="merge*" propagation="REQUIRED" />
           <tx:method name="del*" propagation="REQUIRED" />
           <tx:method name="remove*" propagation="REQUIRED" />
           <tx:method name="put*" propagation="REQUIRED" />
           <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
           <tx:method name="count*" propagation="SUPPORTS" read-only="true" />
          <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
          <tx:method name="list*" propagation="SUPPORTS" read-only="true" />
          <tx:method name="*" propagation="SUPPORTS" read-only="true" />
       </tx:attributes>
</tx:advice>
<aop:config>
       <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service.*.*(..))" />
       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>

該聲明式事務(wù)配置可以應(yīng)付常見(jiàn)的CRUD接口定義,并實(shí)現(xiàn)事務(wù)管理,我們只需修改切入點(diǎn)表達(dá)式來(lái)攔截我們的業(yè)務(wù)實(shí)現(xiàn)從而對(duì)其應(yīng)用事務(wù)屬性就可以了,如果還有更復(fù)雜的事務(wù)屬性直接添加即可,即

如果我們有一個(gè)batchSaveOrUpdate方法需要“REQUIRES_NEW”事務(wù)傳播行為,則直接添加如下配置即可:

java代碼:
1
<tx:method name="batchSaveOrUpdate" propagation="REQUIRES_NEW" />
2、接下來(lái)看一下多事務(wù)語(yǔ)義配置吧,聲明式事務(wù)最佳實(shí)踐中已經(jīng)配置了通用事務(wù)屬性,因此可以針對(duì)需要其他事務(wù)屬性的業(yè)務(wù)方法進(jìn)行特例化配置:

java代碼:
<tx:advice id="noTxAdvice" transaction-manager="txManager">
    <tx:attributes>
           <tx:method name="*" propagation="NEVER" />
    </tx:attributes>
</tx:advice>
<aop:config>
       <aop:pointcut id="noTxPointcut" expression="execution(* cn.javass..util.*.*())" />
       <aop:advisor advice-ref="noTxPointcut" pointcut-ref="noTxAdvice" />
</aop:config>

該聲明將對(duì)切入點(diǎn)匹配的方法所在事務(wù)應(yīng)用“Never”傳播行為。

多事務(wù)語(yǔ)義配置時(shí),切入點(diǎn)一定不要疊加,否則將應(yīng)用兩次事務(wù)屬性,造成不必要的錯(cuò)誤及麻煩。

@Transactional實(shí)現(xiàn)事務(wù)管理

對(duì)聲明式事務(wù)管理,Spring提供基于@Transactional注解方式來(lái)實(shí)現(xiàn),但需要Java 5+。

注解方式是最簡(jiǎn)單的事務(wù)配置方式,可以直接在Java源代碼中聲明事務(wù)屬性,且對(duì)于每一個(gè)業(yè)務(wù)類(lèi)或方法如果需要事務(wù)都必須使用此注解。

接下來(lái)學(xué)習(xí)一下注解事務(wù)的使用吧:

1、定義業(yè)務(wù)邏輯實(shí)現(xiàn):

package cn.javass.spring.chapter9.service.impl;
//省略import
public class AnnotationUserServiceImpl implements IUserService {
    private IUserDao userDao;
    private IAddressService addressService;
    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }
    public void setAddressService(IAddressService addressService) {
        this.addressService = addressService;
    }
    @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)
    @Override
    public void save(final UserModel user) {
        userDao.save(user);
        user.getAddress().setUserId(user.getId());
        addressService.save(user.getAddress());
    }
    @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, readOnly=true)
    @Override
    public int countAll() {
        return userDao.countAll();
    }
}

2、定義配置文件(chapter9/service/ applicationContext-service-annotation.xml):

2.1、XML命名空間定義,定義用于事務(wù)支持的tx命名空間和AOP支持的aop命名空間:

java代碼:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="

    http://www.springframework.org/schema/beans
     
     
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     
     
    http://www.springframework.org/schema/tx
     
     
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     
     
    http://www.springframework.org/schema/aop
     
     
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

2.2、業(yè)務(wù)實(shí)現(xiàn)配置,非常簡(jiǎn)單,使用以前定義的非侵入式業(yè)務(wù)實(shí)現(xiàn):

java代碼:
<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">
    <property name="userDao" ref="userDao"/>
    <property name="addressService" ref="addressService"/>
</bean>
<bean id="addressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">
    <property name="addressDao" ref="addressDao"/>
</bean>

2.3、事務(wù)相關(guān)配置:

java代碼:
1
<tx:annotation-driven transaction-manager="txManager"/>
使用如上配置已支持聲明式事務(wù)。

3、修改測(cè)試方法并測(cè)試該配置方式是否好用:

將TransactionTest 類(lèi)的testServiceTransaction測(cè)試方法拷貝一份命名為testAnntationTransactionTest:



classpath:chapter9/service/applicationContext-service-annotation.xml"

userService.save(user);
try {
    userService.save(user);
    Assert.fail();
} catch (RuntimeException e) {
}
Assert.assertEquals(0, userService.countAll());
Assert.assertEquals(0, addressService.countAll());

4、執(zhí)行測(cè)試,測(cè)試正常通過(guò),說(shuō)明該方式能正常工作,因?yàn)樵贏nnotationAddressServiceImpl類(lèi)的save方法中拋出異常,因此事務(wù)需要回滾,所以?xún)蓚€(gè)countAll操作都返回0。

9.4.7 @Transactional配置詳解
Spring提供的<tx:annotation-driven/>用于開(kāi)啟對(duì)注解事務(wù)管理的支持,從而能識(shí)別Bean類(lèi)上的@Transactional注解元數(shù)據(jù),其具有以下屬性:

transaction-manager:指定事務(wù)管理器名字,默認(rèn)為transactionManager,當(dāng)使用其他名字時(shí)需要明確指定;
proxy-target-class:表示將使用的代碼機(jī)制,默認(rèn)false表示使用JDK代理,如果為true將使用CGLIB代理
order:定義事務(wù)通知順序,默認(rèn)Ordered.LOWEST_PRECEDENCE,表示將順序決定權(quán)交給AOP來(lái)處理。
Spring使用@Transactional 來(lái)指定事務(wù)屬性,可以在接口、類(lèi)或方法上指定,如果類(lèi)和方法上都指定了@Transactional ,則方法上的事務(wù)屬性被優(yōu)先使用,具體屬性如下:

value:指定事務(wù)管理器名字,默認(rèn)使用<tx:annotation-driven/>指定的事務(wù)管理器,用于支持多事務(wù)管理器環(huán)境;
propagation:指定事務(wù)傳播行為,默認(rèn)為Required,使用Propagation.REQUIRED指定;
isolation:指定事務(wù)隔離級(jí)別,默認(rèn)為“DEFAULT”,使用Isolation.DEFAULT指定;
readOnly:指定事務(wù)是否只讀,默認(rèn)false表示事務(wù)非只讀;
timeout:指定事務(wù)超時(shí)時(shí)間,以秒為單位,默認(rèn)-1表示事務(wù)超時(shí)將依賴(lài)于底層事務(wù)系統(tǒng);
rollbackFor:指定一組異常類(lèi),遇到該類(lèi)異常將回滾事務(wù);
rollbackForClassname:指定一組異常類(lèi)名字,其含義與<tx:method>中的rollback-for屬性語(yǔ)義完全一樣;
noRollbackFor:指定一組異常類(lèi),即使遇到該類(lèi)異常也將提交事務(wù),即不回滾事務(wù);
noRollbackForClassname:指定一組異常類(lèi)名字,其含義與<tx:method>中的no-rollback-for屬性語(yǔ)義完全一樣;
Spring提供的@Transactional 注解事務(wù)管理內(nèi)部同樣利用環(huán)繞通知TransactionInterceptor實(shí)現(xiàn)事務(wù)的開(kāi)啟及關(guān)閉。

使用@Transactional注解事務(wù)管理需要特別注意以下幾點(diǎn):

如果在接口、實(shí)現(xiàn)類(lèi)或方法上都指定了@Transactional 注解,則優(yōu)先級(jí)順序?yàn)榉椒?gt;實(shí)現(xiàn)類(lèi)>接口;
建議只在實(shí)現(xiàn)類(lèi)或?qū)崿F(xiàn)類(lèi)的方法上使用@Transactional,而不要在接口上使用,這是因?yàn)槿绻褂肑DK代理機(jī)制是沒(méi)問(wèn)題,因?yàn)槠涫褂没诮涌诘拇?;而使用使用CGLIB代理機(jī)制時(shí)就會(huì)遇到問(wèn)題,因?yàn)槠涫褂没陬?lèi)的代理而不是接口,這是因?yàn)榻涌谏系腀Transactional注解是“不能繼承的”;
具體請(qǐng)參考基于JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理的實(shí)現(xiàn)Spring注解管理事務(wù)(@Trasactional)到底有什么區(qū)別。
在Spring代理機(jī)制下(不管是JDK動(dòng)態(tài)代理還是CGLIB代理),“自我調(diào)用”同樣不會(huì)應(yīng)用相應(yīng)的事務(wù)屬性,其語(yǔ)義和<tx:tags>中一樣;
默認(rèn)只對(duì)RuntimeException異常回滾;
在使用Spring代理時(shí),默認(rèn)只有在public可見(jiàn)度的方法的@Transactional 注解才是有效的,其它可見(jiàn)度(protected、private、包可見(jiàn))的方法上即使有@Transactional 注解也不會(huì)應(yīng)用這些事務(wù)屬性的,Spring也不會(huì)報(bào)錯(cuò),如果你非要使用非公共方法注解事務(wù)管理的話(huà),可考慮使用AspectJ。

微信公眾號(hào)【黃小斜】作者是螞蟻金服 JAVA 工程師,專(zhuān)注于 JAVA
后端技術(shù)棧:SpringBoot、SSM全家桶、MySQL、分布式、中間件、微服務(wù),同時(shí)也懂點(diǎn)投資理財(cái),堅(jiān)持學(xué)習(xí)和寫(xiě)作,相信終身學(xué)習(xí)的力量!關(guān)注公眾號(hào)后回復(fù)”架構(gòu)師“即可領(lǐng)取
Java基礎(chǔ)、進(jìn)階、項(xiàng)目和架構(gòu)師等免費(fèi)學(xué)習(xí)資料,更有數(shù)據(jù)庫(kù)、分布式、微服務(wù)等熱門(mén)技術(shù)學(xué)習(xí)視頻,內(nèi)容豐富,兼顧原理和實(shí)踐,另外也將贈(zèng)送作者原創(chuàng)的Java學(xué)習(xí)指南、Java程序員面試指南等干貨資源

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring 事務(wù)屬性分析 事務(wù)管理對(duì)于企業(yè)應(yīng)用而言至關(guān)重要。它保證了用戶(hù)的每一次操作都是可靠的,即便出現(xiàn)了異常的...
    壹點(diǎn)零閱讀 1,380評(píng)論 0 2
  • 事務(wù)有四個(gè)特性:ACID 原子性(Atomicity):事務(wù)是一個(gè)原子操作,由一系列動(dòng)作組成。事務(wù)的原子性確保動(dòng)作...
    jiangmo閱讀 1,293評(píng)論 0 7
  • 事務(wù): 事務(wù)是邏輯上的一組操作,要么都執(zhí)行,要么都不執(zhí)行。 事物的特性:(ACID) 原子性: 事務(wù)是最小的執(zhí)行單...
    n油炸小朋友閱讀 514評(píng)論 1 1
  • 專(zhuān)業(yè)人做專(zhuān)業(yè)事,一個(gè)人影響一群人帶動(dòng)一批人。人生永遠(yuǎn)保持有夢(mèng)想的狀態(tài),會(huì)收獲不一樣的人生。
    丹亭苑閱讀 239評(píng)論 0 0
  • Mac 使用小技巧 理解 OS X 的 基本結(jié)構(gòu)和特點(diǎn) OS X 采用 Unix 的多用戶(hù)系統(tǒng),所有的用戶(hù)的目錄都...
    李小六_閱讀 4,862評(píng)論 2 7

友情鏈接更多精彩內(nèi)容