轉載地址:http://blog.csdn.net/lxzo123/article/details/5942187
在這篇文章中,我將向你展示如何使用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; // ... @Transactional public 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; // ... @Transactional public void doSomething(... // ...
思考幾秒鐘!你確信你理解了嗎?
- 如果doSomething方法正常執行完畢,沒有拋出任何異常,那麼事務就會提交
- 如果doSomething或它調用的任何方法拋出任何類型的異常被doSomething捕獲並沒有重新拋出,那麼事務被提交
- 如果doSomething或它調用的任何方法拋出了任何的受檢異常並且沒有被捕獲或者是被重新拋出,那麼事務被提交。(因此到到異常代碼處之前的所有都會被提交)
- 如果doSomething或它調用的任何方法拋出了任何的非受檢異常並且沒有被捕獲或者是被重新拋出,那麼事務將被回滾(在該事務中沒有任何東西會被提交到數據庫)
這是默認的行爲。然而,你可能希望能爲特定的情況改變它:
@Transactional(rollbackFor = IOException.class, noRollbackFor = RuntimeException.class) public void doSomething(...
這裏,我要求事務管理器爲IOException回滾異常,而對RuntimeException異常不回滾。
注意:如果你使用XML來設置事務,你可以配置這些規則。