事務管理對於企業應用來講是相當重要的,即便出現異常狀況,它也能夠保證數據的一致性。
Spring Framework對事務管理提供了一致的抽象,其特色以下:html
爲不一樣的事務API提供一致的編程模型,好比JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)java
支持聲明式事務管理,特別是基於註解的聲明式事務管理,簡單易用spring
提供比其餘事務API如JTA更簡單的編程式事務管理API數據庫
Spring事務的本質其實就是數據庫對事務的支持,沒有數據庫的事務支持,spring是沒法提供事務功能的。對於純JDBC操做數據庫,想要用到事務,能夠按照如下步驟進行:編程
使用Spring的事務管理功能後,咱們能夠再也不寫步驟 2 和 4 的代碼,而是由Spirng 自動完成。 那麼Spring是如何在咱們書寫的 CRUD 以前和以後開啓事務和關閉事務的呢?解決這個問題,也就能夠從總體上理解Spring的事務管理實現原理了。併發
注意: 性能
默認狀況下,數據庫處於自動提交模式(即con.setAutoCommit(true))。每一條語句處於一個單獨的事務中,在這條語句執行完畢時,若是執行成功則隱式的提交事務,若是執行失敗則隱式的回滾事務。this
對於正常的事務管理,是一組相關的操做處於一個事務之中,所以必須關閉數據庫的自動提交模式。不過,這個咱們不用擔憂,spring會將底層鏈接的自動提交特性設置爲false。spa
下面是Spring底層實現該功能的代碼:debug
// switch to manual commit if necessary. this is very expensive in some jdbc drivers, // so we don't want to do it unnecessarily (for example if we've explicitly // configured the connection pool to set it already). if (con.getautocommit()) { txobject.setmustrestoreautocommit(true); if (logger.isdebugenabled()) { logger.debug("switching jdbc connection [" + con + "] to manual commit"); } con.setautocommit(false); }
Spring支持編程式事務管理和聲明式事務管理兩種方式。
編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,Spring推薦使用TransactionTemplate。
聲明式事務管理創建在AOP之上的。其本質是對方法先後進行攔截,而後在目標方法開始以前建立或者加入一個事務,在執行完目標方法以後根據執行狀況提交或者回滾事務。聲明式事務最大的優勢就是不須要經過編程的方式管理事務,這樣就不須要在業務邏輯代碼中摻瑣事務管理的代碼,只需在配置文件中作相關的事務規則聲明(或經過基於@Transactional註解的方式),即可以將事務規則應用到業務邏輯中。
顯然聲明式事務管理要優於編程式事務管理,這正是Spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上註解就能夠得到徹底的事務支持。和編程式事務相比,聲明式事務惟一不足地方是,後者的最細粒度只能做用到方法級別,沒法作到像編程式事務那樣能夠做用到代碼塊級別。可是即使有這樣的需求,也存在不少變通的方法,好比,能夠將須要進行事務管理的代碼塊獨立爲方法等等。
聲明式事務管理也有兩種經常使用的方式,一種是基於tx和aop名字空間的xml配置文件,另外一種就是基於@Transactional註解。顯然基於註解的方式更簡單易用,更清爽。
spring事務的傳播屬性:存在多個事務同時存在的時候,spring應該如何處理這些事務的行爲。這些屬性在TransactionDefinition中定義,具體常量的解釋見下表:
常量名稱 | 常量解釋 |
PROPAGATION_REQUIRED | 支持當前事務,若是當前沒有事務,就新建一個事務。這是最多見的選擇,也是 Spring 默認的事務的傳播。 |
PROPAGATION_REQUIRES_NEW | 新建事務,若是當前存在事務,把當前事務掛起。新建的事務將和被掛起的事務沒有任何關係,是兩個獨立的事務,外層事務失敗回滾以後,不能回滾內層事務執行的結果,內層事務失敗拋出異常,外層事務捕獲,也能夠不處理回滾操做 |
PROPAGATION_SUPPORTS | 支持當前事務,若是當前沒有事務,就以非事務方式執行。 |
PROPAGATION_MANDATORY | 支持當前事務,若是當前沒有事務,就拋出異常。 |
PROPAGATION_NOT_SUPPORTED | 以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。 |
PROPAGATION_NEVER | 以非事務方式執行,若是當前存在事務,則拋出異常。 |
PROPAGATION_NESTED | 若是一個活動的事務存在,則運行在一個嵌套的事務中。若是沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個能夠回滾的保存點。內部事務的回滾不會對外部事務形成影響。它只對DataSourceTransactionManager事務管理器起效。 |
隔離級別 | 隔離級別的值 | 致使的問題 |
Read-Uncommitted | 0 | 致使髒讀 |
Read-Committed | 1 | 避免髒讀,容許不可重複讀和幻讀 |
Repeatable-Read | 2 | 避免髒讀,不可重複讀,容許幻讀 |
Serializable | 3 | 串行化讀,事務只能一個一個執行,避免了髒讀、不可重複讀、幻讀。執行效率慢,使用時慎重 |
髒讀:一事務對數據進行了增刪改,但未提交,另外一事務能夠讀取到未提交的數據。若是第一個事務這時候回滾了,那麼第二個事務就讀到了髒數據。
不可重複讀:一個事務中發生了兩次讀操做,第一次讀操做和第二次操做之間,另一個事務對數據進行了修改,這時候兩次讀取的數據是不一致的。
幻讀:第一個事務對必定範圍的數據進行批量修改,第二個事務在這個範圍增長一條數據,這時候第一個事務就會丟失對新增數據的修改。
總結:
隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。
大多數的數據庫默認隔離級別爲 Read Commited,好比 SqlServer、Oracle
少數數據庫默認隔離級別爲:Repeatable Read 好比: MySQL InnoDB