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> |