轉自:http://www.xdocs.org/?author=1
在這篇文章中,我將向你展示如何使用Spring處理交易管理。
Spring交易管理的優勢
- 非常容易使用,不需要一點底層事務API的知識
- 你的交易管理代碼將與具體的事務實現分離
- 提供了註解和XML的配置方式
- 並不需要運行在伺服器上
非常好!讓我們開始一些基礎資訊,如果你狠瞭解事務的話,你可以忽略它。
概覽
事務 保證你應用中(資料來源)的資料保持一致。同樣,你也需要熟悉ACID概念。
現在,在Java中,你可以使用純SQL來處理事務、使用純JDBC、Hibernate或更高層面的EJB或者Spring。
EJB要求一個應用伺服器,但是我們的應用不需要。
編程式 vs. 聲明式
Spring提供了兩種處理事務的方式:編程式和聲明式。如果你熟悉EJB的交易處理,這兩種對應的方式應該是bean管理和容器管理的交易管理。
編程式 意味著你需要在業務代碼中加入交易管理代碼。這樣非常靈活,但是維護起來比較麻煩。
聲明式 意味著你將交易管理代碼從業務代碼中分離,只需要使用基於註解或XML的配置即可。
我們說:
- 編程式管理在開發階段更加靈活但是在應用生命週期中則不是很靈活
- 聲明式管理在開發階段不是很靈活但是在應用生命週期中則更加靈活
在本文中…
我們將關注聲明式交易管理.
好吧,關於我們將希望實現的和如何來實現做一個簡短的討論。
目標:
- 一個案頭應用(也就是說,不需要伺服器)
- 使用最少的XML進行聲明式交易管理
這意味著:
- 使用Eclipse和SpringIDE建立的Maven項目
- Spring交易管理
- 帶有啟用註解的Java(我將使用Java6)
- JPA 2.0
交易管理
首先,我們必須確定我們希望使用基於註解的交易管理。
當然,交易管理員本身:
我們使用的交易管理員是JpaTransactionManager。當然也有其他的管理器:
DataSourceTransactionManager – 用於單獨的資料來源和JDBC
- JtaTransactionManager – 用於JTA
- HibernateTransactionManager – 用於Hibernate
它引用了一個名為entityManagerFactory的bean。如下:
基於註解的交易管理
@Transactional是你可以在任何bean的任何方法或bean本身上使用的註解。
import org.springframework.transaction.annotation.Transactional;// ...@Transactionalpublic void doSomething(...// ...
如果你將該註解用於bean上(也就是類層級),其中的每個公用的方法都將是受事務控制的。
注意:該註解僅僅對受Spring管理的資料來源有效。如果你從其他的資料來源擷取資料,@Transactional將沒有任何作用。
在哪裡放@Transactional?
你需要將該註解放到你的商務邏輯方法(服務方法)上,而不是DAO方法中,這是一個規則。通常一個業務方法將調用很多DAO方法,並且這些方法只有在組合在一起時才有意義。
事務範圍
每當一個事務方法被調用時,都要做一個決定,如何處理該事務。建立一個新事務?如果已存在,使用已有的,或者是建立新事務?使用已存在的事務,如果存在,否則失敗?
為了使你可以規定它,事務範圍(propagation)行為被添加了。
import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;// ...@Transactional(propagation = Propagation.REQUIRED)public void doSomething(...// ...
如下的行為:
- REQUIRED – 使用已存在的事務。如果不存在,建立一個新事務
- REQUIRES_NEW – 必須啟用新事務。如果已存在,那麼已存在的需要被掛起
- SUPPORTS – 如果已存在事務,在它裡面運行代碼。如果不存在,那麼就不使用事務
- NOT_SUPPORTED – 必須不能在事務中運行。如果已存在事務,那麼需要被掛起
- MANDATORY – 必須運行於事務中。如果沒有已存在事務,它將拋出異常
- NEVER – 一定不能運行於事務中。如果已存在事務,它將拋出異常
- NESTED – 如果已存在事務,它將在嵌套事務中運行。否則,它運行於自己的事務中
對每個方法採用何種事務範圍就取決於你了。REQUIRED是預設的。
注意:如果你使用XML來設定事務,你可以使用XML來配置這些行為。
事務隔離
並發事務引發的問題可能很難審查。
- 丟失更新 – 兩個事務都修改一行資料,如果第二個事務失敗,那麼所有的更新都將丟失
- 髒讀 – 讀取了未提交的改變
- 非重複讀 – 一個事務對同一行資料讀了兩次,每次擷取了不同的資料
- 幻讀(Phantom read) – 與前一個類似,只是行號不同
現在,該問題最好的解決方案是最大化事務隔離,但是在現實中,這將導致太多的資源消耗並可能導致死結。因此,你需要如下五個隔離等級(第五個實際是最大化的事務隔離等級)。
- DEFAULT – 使用預設的資料庫隔離等級
- READ_UNCOMMITTED – 髒讀、非重複讀、幻讀等問題可能會出現,但是不會出現更新丟失
- READ_COMMITTED – 非重複讀、幻讀可能出現
- REPEATABLE_READ – 幻讀可能出現
- SERIALIZABLE – 所有的問題都可以避免!但是效能最差
預設的選擇是DEFAULT.
HSQLDB 2.0支援READ_COMMITTED和SERIALIZABLE levels (HSQLDB FAQ).
… 那麼復原!交易回復
使用Spring交易管理,自動復原預設的行為是:只有非受檢異常引起復原。非受檢異常是 RuntimeExceptions 和 Errors。
import org.springframework.transaction.annotation.Transactional;// ...@Transactionalpublic void doSomething(...// ...
思考幾秒鐘!你確信你理解了嗎?
- 如果doSomething方法正常執行完畢,沒有拋出任何異常,那麼事務就會提交
- 如果doSomething或它調用的任何方法拋出任何類型的異常被doSomething捕獲並沒有重新拋出,那麼事務被提交
- 如果doSomething或它調用的任何方法拋出了任何的受檢異常並且沒有被捕獲或者是被重新拋出,那麼事務被提交。(因此到到異常代碼處之前的所有都會被提交)
- 如果doSomething或它調用的任何方法拋出了任何的非受檢異常並且沒有被捕獲或者是被重新拋出,那麼事務將被復原(在該事務中沒有任何東西會被提交到資料庫)
這是預設的行為。然而,你可能希望能為特定的情況改變它:
@Transactional(rollbackFor = IOException.class, noRollbackFor = RuntimeException.class)public void doSomething(...
這裡,我要求交易管理員為IOException復原異常,而對RuntimeException異常不復原。
注意:如果你使用XML來設定事務,你可以配置這些規則。