Spring框架 第8章 聲明式事務

第8章 ?聲明式事務

8.1事務概述

? 1)? 在JavaEE企業(yè)級開發(fā)的應用領域,為了保證數(shù)據(jù)的完整性一致性,必須引入數(shù)據(jù)庫事務的概念,所以事務管理是企業(yè)級應用程序開發(fā)中必不可少的技術。

? 2)? 事務就是一組由于邏輯上緊密關聯(lián)而合并成一個整體(工作單元)的多個數(shù)據(jù)庫操作,這些操作要么都執(zhí)行要么都不執(zhí)行。

? 3)?事務的四個關鍵屬性(ACID)

原子性(atomicity):“原子”的本意是“不可再分”,事務的原子性表現(xiàn)為一個事務中涉及到的多個操作在邏輯上缺一不可。事務的原子性要求事務中的所有操作要么都執(zhí)行,要么都不執(zhí)行。

一致性(consistency):“一致”指的是數(shù)據(jù)的一致,具體是指:所有數(shù)據(jù)都處于滿足業(yè)務規(guī)則的一致性狀態(tài)。一致性原則要求:一個事務中不管涉及到多少個操作,都必須保證事務執(zhí)行之前數(shù)據(jù)是正確的,事務執(zhí)行之后數(shù)據(jù)仍然是正確的。如果一個事務在執(zhí)行的過程中,其中某一個或某幾個操作失敗了,則必須將其他所有操作撤銷,將數(shù)據(jù)恢復到事務執(zhí)行之前的狀態(tài),這就是回滾

隔離性(isolation):在應用程序實際運行過程中,事務往往是并發(fā)執(zhí)行的,所以很有可能有許多事務同時處理相同的數(shù)據(jù),因此每個事務都應該與其他事務隔離開來,防止數(shù)據(jù)損壞。隔離性原則要求多個事務在并發(fā)執(zhí)行過程中不會互相干擾。

持久性(durability):持久性原則要求事務執(zhí)行完成后,對數(shù)據(jù)的修改永久的保存下來,不會因各種系統(tǒng)錯誤或其他意外情況而受到影響。通常情況下,事務對數(shù)據(jù)的修改應該被寫入到持久化存儲器中。

8.2 Spring事務管理

8.2.1編程式事務管理

? 1)? 使用原生的JDBC API進行事務管理

①獲取數(shù)據(jù)庫連接Connection對象

②取消事務的自動提交

③執(zhí)行操作

④正常完成操作時手動提交事務

⑤執(zhí)行失敗時回滾事務

⑥關閉相關資源

? 2)? 評價

使用原生的JDBC API實現(xiàn)事務管理是所有事務管理方式的基石,同時也是最典型 的編程式事務管理。編程式事務管理需要將事務管理代碼嵌入到業(yè)務方法中來控制事務 的提交和回滾。在使用編程的方式管理事務時,必須在每個事務操作中包含額外的事務 管理代碼。相對于核心業(yè)務而言,事務管理的代碼顯然屬于非核心業(yè)務,如果多個模塊 都使用同樣模式的代碼進行事務管理,顯然會造成較大程度的代碼冗余

8.2.2 聲明式事務管理

大多數(shù)情況下聲明式事務比編程式事務管理更好:它將事務管理代碼從業(yè)務方法中分離出來,以聲明的方式來實現(xiàn)事務管理。

事務管理代碼的固定模式作為一種橫切關注點,可以通過AOP方法模塊化,進而借助Spring AOP框架實現(xiàn)聲明式事務管理。

Spring在不同的事務管理API之上定義了一個抽象層,通過配置的方式使其生效,從而讓應用程序開發(fā)人員不必了解事務管理API的底層實現(xiàn)細節(jié),就可以使用Spring的事務管理機制。

Spring既支持編程式事務管理,也支持聲明式的事務管理。

8.2.3 Spring提供的事務管理器

Spring從不同的事務管理API中抽象出了一整套事務管理機制,讓事務管理代碼從特定的事務技術中獨立出來。開發(fā)人員通過配置的方式進行事務管理,而不必了解其底層是如何實現(xiàn)的。

Spring的核心事務管理抽象是PlatformTransactionManager。它為事務管理封裝了一組獨立于技術的方法。無論使用Spring的哪種事務管理策略(編程式或聲明式),事務管理器都是必須的。

事務管理器可以以普通的bean的形式聲明在Spring IOC容器中。

8.2.4事務管理器的主要實現(xiàn)

? 1)? DataSourceTransactionManager:在應用程序中只需要處理一個數(shù)據(jù)源,而且通過JDBC存取。

? ?2)? JtaTransactionManager:在JavaEE應用服務器上用JTA(Java Transaction API)進行事務管理

? 3)? HibernateTransactionManager:用Hibernate框架存取數(shù)據(jù)庫

8.3 測試數(shù)據(jù)準備

8.3.1 需求

8.3.2 數(shù)據(jù)庫表

CREATE TABLE book?(

??isbn VARCHAR (50) PRIMARY KEY,

??book_name VARCHAR (100),

??price INT

) ;

 

CREATE TABLE book_stock?(

??isbn VARCHAR (50) PRIMARY KEY,

??stock INT,

??CHECK (stock > 0)

) ;

 

CREATE TABLE account?(

??username VARCHAR (50) PRIMARY KEY,

??balance INT,

??CHECK (balance > 0)

) ;

 

INSERT INTO account (`username`,`balance`) VALUES ('Tom',100000);

INSERT INTO account (`username`,`balance`) VALUES ('Jerry',150000);

 

INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-001','book01',100);

INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-002','book02',200);

INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-003','book03',300);

INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-004','book04',400);

INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-005','book05',500);

 

INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-001',1000);

INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-002',2000);

INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-003',3000);

INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-004',4000);

INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-005',5000);

8.4 初步實現(xiàn)

? 1)? 配置文件

<!-- 配置事務管理器 -->

<bean?id="transactionManager"?

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property?name="dataSource"?ref="dataSource"/> ??

</bean>

 

<!-- 啟用事務注解 -->

<tx:annotation-driven?transaction-manager="transactionManager"/>

? 2)? 在需要進行事務控制的方法上加注解 @Transactional

8.5 事務的傳播行為

8.5.1 簡介

當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續(xù)在現(xiàn)有事務中運行,也可能開啟一個新事務,并在自己的事務中運行。

事務的傳播行為可以由傳播屬性指定。Spring定義了7種類傳播行為。

事務傳播屬性可以在@Transactional注解的propagation屬性中定義。

8.5.2 測試? ?1) . 說明

①REQUIRED傳播行為

當bookService的purchase()方法被另一個事務方法checkout()調用時,它默認會在現(xiàn)有的事務內(nèi)運行。這個默認的傳播行為就是REQUIRED。因此在checkout()方法的開始和終止邊界內(nèi)只有一個事務。這個事務只在checkout()方法結束的時候被提交,結果用戶一本書都買不了。

②. REQUIRES_NEW傳播行為

表示該方法必須啟動一個新事務,并在自己的事務內(nèi)運行。如果有事務在運行,就應該先掛起它。

8.5.3 補充

在Spring 2.x事務通知中,可以像下面這樣在<tx:method>元素中設定傳播事務屬性。

8.6 事務的隔離級別

8.6.1 數(shù)據(jù)庫事務并發(fā)問題

假設現(xiàn)在有兩個事務:Transaction01和Transaction02并發(fā)執(zhí)行。

? 1)? 臟讀

①Transaction01將某條記錄的AGE值從20修改為30。

②Transaction02讀取了Transaction01更新后的值:30。

③Transaction01回滾,AGE值恢復到了20。

④Transaction02讀取到的30就是一個無效的值。

? 2)? 不可重復讀

①Transaction01讀取了AGE值為20。

②Transaction02將AGE值修改為30。

③Transaction01再次讀取AGE值為30,和第一次讀取不一致。

? 3)? 幻讀

①Transaction01讀取了STUDENT表中的一部分數(shù)據(jù)。

②Transaction02向STUDENT表中插入了新的行。

③Transaction01讀取了STUDENT表時,多出了一些行。

8.6.2 隔離級別

數(shù)據(jù)庫系統(tǒng)必須具有隔離并發(fā)運行各個事務的能力,使它們不會相互影響,避免各種并發(fā)問題。一個事務與其他事務隔離的程度稱為隔離級別。SQL標準中規(guī)定了多種事務隔離級別,不同隔離級別對應不同的干擾程度,隔離級別越高,數(shù)據(jù)一致性就越好,但并發(fā)性越弱。

? 1)? 讀未提交:READ UNCOMMITTED

允許Transaction01讀取Transaction02未提交的修改。

? 2)? 讀已提交:READ COMMITTED

???要求Transaction01只能讀取Transaction02已提交的修改。

? 3)? 可重復讀:REPEATABLE READ

???確保Transaction01可以多次從一個字段中讀取到相同的值,即Transaction01執(zhí)行期間禁止其它事務對這個字段進行更新。

? 4)? 串行化:SERIALIZABLE

???確保Transaction01可以多次從一個表中讀取到相同的行,在Transaction01執(zhí)行期間,禁止其它事務對這個表進行添加、更新、刪除操作??梢员苊馊魏尾l(fā)問題,但性能十分低下。

? 5)? 各個隔離級別解決并發(fā)問題的能力見下表

 

臟讀

不可重復讀

幻讀

READ UNCOMMITTED

READ COMMITTED

REPEATABLE READ

SERIALIZABLE

? 6)? 各種數(shù)據(jù)庫產(chǎn)品對事務隔離級別的支持程度

 

Oracle

MySQL

READ UNCOMMITTED

×

READ COMMITTED

√(默認)

REPEATABLE READ

×

√(默認)

SERIALIZABLE

8.6.3 在Spring中指定事務隔離級別

? 1)? 注解

用@Transactional注解聲明式地管理事務時可以在@Transactional的isolation屬性中設置隔離級別

? 2)? XML

在Spring 2.x事務通知中,可以在<tx:method>元素中指定隔離級別

8.7 觸發(fā)事務回滾的異常

8.7.1默認情況

捕獲到RuntimeException或Error時回滾,而捕獲到編譯時異常不回滾。

8.7.2設置途經(jīng)

? 1)? 注解@Transactional 注解

① rollbackFor屬性:指定遇到時必須進行回滾的異常類型,可以為多個

② noRollbackFor屬性:指定遇到時不回滾的異常類型,可以為多個? 2)? XML

在Spring 2.x事務通知中,可以在<tx:method>元素中指定回滾規(guī)則。如果有不止一種異常則用逗號分隔。

8.8 事務的超時和只讀屬性

8.8.1簡介

由于事務可以在行和表上獲得鎖,因此長事務會占用資源,并對整體性能產(chǎn)生影響。如果一個事務只讀取數(shù)據(jù)但不做修改,數(shù)據(jù)庫引擎可以對這個事務進行優(yōu)化。超時事務屬性:事務在強制回滾之前可以保持多久。這樣可以防止長期運行的事務占用資源。

只讀事務屬性: 表示這個事務只讀取數(shù)據(jù)但不更新數(shù)據(jù), 這樣可以幫助數(shù)據(jù)庫引擎優(yōu)化事務。

8.8.2設置

? 1)? 注解

@Transaction注解? 2)? XML

在Spring 2.x事務通知中,超時和只讀屬性可以在<tx:method>元素中進行指定

8.9 基于XML文檔的聲明式事務配置

<!-- 配置事務切面 -->

<aop:config>

<aop:pointcut

expression="execution(* com.atguigu.tx.component.service.BookShopServiceImpl.purchase(..))"

id="txPointCut"/>

<!-- 將切入點表達式和事務屬性配置關聯(lián)到一起 -->

<aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/>

</aop:config>

<!-- 配置基于XML的聲明式事務 ?-->

<tx:advice id="myTx" transaction-manager="transactionManager">

<tx:attributes>

<!-- 設置具體方法的事務屬性 -->

<tx:method name="find*" read-only="true"/>

<tx:method name="get*" read-only="true"/>

<tx:method name="purchase"

isolation="READ_COMMITTED"

no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"

propagation="REQUIRES_NEW"

read-only="false"

timeout="10"/>

</tx:attributes>

</tx:advice>