經過這段時間的對Java事務機制的學習,用這篇文章作個階段性的總結,後續若是有時間,還能夠深刻學習和研究下分佈式事務的補償機制(目前中國只有阿里巴巴在這方面有成熟的研究成果),後續的學習成果,我也會補充到該系列文章中html
1、什麼是JAVA事務java
一般的觀念認爲,事務僅與數據庫相關。mysql
事務必須服從ISO/IEC所制定的ACID原則。ACID是原子性(atomicity)、一致性(consistency)、隔離性 (isolation)和持久性(durability)的縮寫。事務的原子性表示事務執行過程當中的任何失敗都將致使事務所作的任何修改失效。一致性表示 當事務執行失敗時,全部被該事務影響的數據都應該恢復到事務執行前的狀態。隔離性表示在事務執行過程當中對數據的修改,在事務提交以前對其餘事務不可見。持 久性表示已提交的數據在事務執行失敗時,數據的狀態都應該正確。程序員
通俗的理解,事務是一組原子操做單元,從數據庫角度說,就是一組SQL指令,要麼所有執行成功,若由於某個緣由其中一條指令執行有錯誤,則撤銷先前執行過的全部指令。更簡答的說就是:要麼所有執行成功,要麼撤銷不執行。spring
既然事務的概念從數據庫而來,那Java事務是什麼?之間有什麼聯繫?sql
實際上,一個Java應用系統,若是要操做數據庫,則經過JDBC來實現的。增長、修改、刪除都是經過相應方法間接來實現的,事務的控制也相應轉移到Java程序代碼中。所以,數據庫操做的事務習慣上就稱爲Java事務。數據庫
2、爲何須要Java事務編程
事務是爲解決數據安全操做提出的,事務控制實際上就是控制數據的安全訪問。舉一個簡單例子:好比銀行轉賬業務,帳戶A要將本身帳戶上的1000元轉到B帳 戶下面,A帳戶餘額首先要減去1000元,而後B帳戶要增長1000元。假如在中間網絡出現了問題,A帳戶減去1000元已經結束,B由於網絡中斷而操做 失敗,那麼整個業務失敗,必須作出控制,要求A帳戶轉賬業務撤銷。這才能保證業務的正確性,完成這個操做就須要事務,將A帳戶資金減小和B帳戶資金增長方 到一個事務裏面,要麼所有執行成功,要麼操做所有撤銷,這樣就保持了數據的安全性。安全
3、Java事務的類型服務器
Java事務的類型有三種:JDBC事務、JTA(Java Transaction API)事務、容器事務。
一、JDBC事務
JDBC 事務是用 Connection 對象控制的。JDBC Connection 接口( java.sql.Connection )提供了兩種事務模式:自動提交和手工提交。 java.sql.Connection 提供瞭如下控制事務的方法:
public void setAutoCommit(boolean)
public boolean getAutoCommit()
public void commit()
public void rollback()
使用 JDBC 事務界定時,您能夠將多個 SQL 語句結合到一個事務中。JDBC 事務的一個缺點是事務的範圍侷限於一個數據庫鏈接。一個 JDBC 事務不能跨越多個數據庫。
二、JTA(java Transaction API)事務
JTA是一種高層的,與實現無關的,與協議無關的API,應用程序和應用服務器可使用JTA來訪問事務。
JTA容許應用程序執行分佈式事務處理——在兩個或多個網絡計算機資源上訪問而且更新數據,這些數據能夠分佈在多個數據庫上。JDBC驅動程序的JTA支持極大地加強了數據訪問能力。
若是計劃用 JTA 界定事務,那麼就須要有一個實現 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驅動程序。一個實現了這些接口的驅動程序將能夠參與 JTA 事務。一個 XADataSource 對象就是一個 XAConnection 對象的工廠。 XAConnection s 是參與 JTA 事務的 JDBC 鏈接。
您將須要用應用服務器的管理工具設置 XADataSource .從應用服務器和 JDBC 驅動程序的文檔中能夠了解到相關的指導。
J2EE應用程序用 JNDI 查詢數據源。一旦應用程序找到了數據源對象,它就調用 javax.sql.DataSource.getConnection() 以得到到數據庫的鏈接。
XA 鏈接與非 XA 鏈接不一樣。必定要記住 XA 鏈接參與了 JTA 事務。這意味着 XA 鏈接不支持 JDBC 的自動提交功能。同時,應用程序必定不要對 XA 鏈接調用 java.sql.Connection.commit() 或者 java.sql.Connection.rollback() .
相反,應用程序應該使用 UserTransaction.begin()、 UserTransaction.commit() 和 serTransaction.rollback() .
做爲java平臺上事務規範JTA(Java Transaction API)也定義了對XA事務的支持,實際上,JTA是基於XA架構上建模的,在JTA 中,事務管理器抽象爲javax.transaction.TransactionManager接口,並經過底層事務服務(即JTS)實現。像不少其餘的java規範同樣,JTA僅僅定義了接口,具體的實現則是由供應商(如J2EE廠商)負責提供,目前JTA的實現主要由如下幾種:
1.J2EE容器所提供的JTA實現(JBoss)
2.獨立的JTA實現:如JOTM,Atomikos.這些實現能夠應用在那些不使用J2EE應用服務器的環境裏用以提供分佈事事務保證。如Tomcat,Jetty以及普通的java應用。
3.兩階段提交
全部關於分佈式事務的介紹中都必然會講到兩階段提交,由於它是實現XA分佈式事務的關鍵(確切地說:兩階段提交主要保證了分佈式事務的原子性:即全部結點要麼全作要麼全不作)。所謂的兩個階段是指:第一階段:準備階段和第二階段:提交階段。
3.1.準備階段:事務協調者(事務管理器)給每一個參與者(資源管理器)發送Prepare消息,每一個參與者要麼直接返回失敗(如權限驗證失敗),要麼在本地執行事務,寫本地的redo和undo日誌,但不提交,到達一種「萬事俱備,只欠東風」的狀態。(關於每個參與者在準備階段具體作了什麼目前我尚未參考到確切的資料,可是有一點很是肯定:參與者在準備階段完成了幾乎全部正式提交的動做,有的材料上說是進行了「試探性的提交」,只保留了最後一步耗時很是短暫的正式提交操做給第二階段執行。)
3.2.提交階段:若是協調者收到了參與者的失敗消息或者超時,直接給每一個參與者發送回滾(Rollback)消息;不然,發送提交(Commit)消息;參與者根據協調者的指令執行提交或者回滾操做,釋放全部事務處理過程當中使用的鎖資源。(注意:必須在最後階段釋放鎖資源)
將提交分紅兩階段進行的目的很明確,就是儘量晚地提交事務,讓事務在提交前儘量地完成全部能完成的工做,這樣,最後的提交階段將是一個耗時極短的微小操做,這種操做在一個分佈式系統中失敗的機率是很是小的,也就是所謂的「網絡通信危險期」很是的短暫,這是兩階段提交確保分佈式事務原子性的關鍵所在。(惟一理論上兩階段提交出現問題的狀況是當協調者發出提交指令後當機並出現磁盤故障等永久性錯誤,致使事務不可追蹤和恢復)
從兩階段提交的工做方式來看,很顯然,在提交事務的過程當中須要在多個節點之間進行協調,而各節點對鎖資源的釋放必須等到事務最終提交時,這樣,比起一階段提交,兩階段提交在執行一樣的事務時會消耗更多時間。事務執行時間的延長意味着鎖資源發生衝突的機率增長,當事務的併發量達到必定數量的時候,就會出現大量事務積壓甚至出現死鎖,系統性能就會嚴重下滑。這就是使用XA事務的反作用
4.一階段提交(Best Efforts 1PC模式)
不像兩階段提交那樣複雜,一階段提交很是直白,就是從應用程序向數據庫發出提交請求到數據庫完成提交或回滾以後將結果返回給應用程序的過程。一階段提交不須要「協調者」角色,各結點之間不存在協調操做,所以其事務執行時間比兩階段提交要短,可是提交的「危險期」是每個事務的實際提交時間,相比於兩階段提交,一階段提交出如今「不一致」的機率就變大了。可是咱們必須注意到:只有當基礎設施出現問題的時候(如網絡中斷,當機等),一階段提交纔可能會出現「不一致」的狀況,相比它的性能優點,不少團隊都會選擇這一方案。關於在spring環境下如何實現一階段提交,有一篇很是優秀的文章值得參考:http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html?page=5
三、容器事務
容器事務主要是J2EE應用服務器提供的,容器事務大可能是基於JTA完成,這是一個基於JNDI的,至關複雜的API實現。相對編碼實現JTA事務管理, 咱們能夠經過EJB容器提供的容器事務管理機制(CMT)完成同一個功能,這項功能由J2EE應用服務器提供。這使得咱們能夠簡單的指定將哪一個方法加入事 務,一旦指定,容器將負責事務管理任務。這是咱們土建的解決方式,由於經過這種方式咱們能夠將事務代碼排除在邏輯編碼以外,同時將全部困難交給J2EE容 器去解決。使用EJB CMT的另一個好處就是程序員無需關心JTA API的編碼,不過,理論上咱們必須使用EJB,而且龐大而複雜,使用起來極其麻煩,基本上已經沒有人使用被淘汰了
4、三種Java事務差別
一、JDBC事務控制的侷限性在一個數據庫鏈接內,可是其使用簡單。
二、JTA事務的功能強大,事務能夠跨越多個數據庫或多個DAO,使用也比較複雜。
三、容器事務,主要指的是J2EE應用服務器提供的事務管理,侷限於EJB應用使用。
5、總結
Java事務控制是構建J2EE應用不可缺乏的一部分,合理選擇應用何種事務對整個應用系統來講相當重要。通常說來,在單個JDBC 鏈接鏈接的狀況下能夠選擇JDBC事務,在跨多個鏈接或者數據庫狀況下,須要選擇使用JTA事務,若是用到了EJB,則能夠考慮使用EJB容器事務
事務的特性:
1) 原子性(atomicity):事務是數據庫的邏輯工做單位,並且是必須是原子工做單位,對於其數據修改,要麼所有執行,要麼所有不執行。
2) 一致性(consistency):事務在完成時,必須是全部的數據都保持一致狀態。在相關數據庫中,全部規則都必須應用於事務的修改,以保持全部數據的完整性。
3) 隔離性(isolation):一個事務的執行不能被其餘事務所影響。
4) 持久性(durability):一個事務一旦提交,事物的操做便永久性的保存在DB中。即便此時再執行回滾操做也不能撤消所作的更改。
事務(Transaction):是併發控制的單元,是用戶定義的一個操做序列。這些操做要麼都作,要麼都不作,是一個不可分割的工做單位。經過事務,sql server 能將邏輯相關的一組操做綁定在一塊兒,以便服務器 保持數據的完整性。事務一般是以begin transaction開始,以commit或rollback結束。Commint表示提交,即提交事務的全部操做。具體地說就是將事務中全部對數據的更新寫回到磁盤上的物理數據庫中去,事務正常結束。Rollback表示回滾,即在事務運行的過程當中發生了某種故障,事務不能繼續進行,系統將事務中對數據庫的全部已完成的操做所有撤消,滾回到事務開始的狀態。
自動提交事務:每條單獨的語句都是一個事務。每一個語句後都隱含一個commit。 (默認)
顯式事務:以begin transaction顯示開始,以commit或rollback結束。
隱式事務:當鏈接以隱式事務模式進行操做時,sql server數據庫引擎實例將在提交或回滾當前事務後自動啓動新事務。無須描述事物的開始,只需提交或回滾每一個事務。但每一個事務仍以commit或rollback顯式結束。鏈接將隱性事務模式設置爲打開以後,當數據庫引擎實例首次執行下列任何語句時,都會自動啓動一個隱式事務:alter table,insert,create,open ,delete,revoke ,drop,select, fetch ,truncate table,grant,update在發出commit或rollback語句以前,該事務將一直保持有效。在第一個事務被提交或回滾以後,下次當鏈接執行以上任何語句時,數據庫引擎實例都將自動啓動一個新事務。該實例將不斷地生成隱性事務鏈,直到隱性事務模式關閉爲止。
Java JDBC事務機制
首先,咱們來看看現有JDBC操做會給咱們打來什麼重大問題,好比有一個業務:當咱們修改一個信息後再去查詢這個信息,看是這是一個簡單的業務,實現起來也很是容易,但當這個業務放在多線程高併發的平臺下,問題天然就出現了,好比當咱們執行了一個修改後,在執行查詢以前有一個線程也執行了修改語句,這是咱們再執行查詢,看到的信息就有可能與咱們修改的不一樣,爲了解決這一問題,咱們必須引入JDBC事務機制,其實代碼實現上很簡單,一下給出一個原理實現例子供你們參考:
private Connection conn = null; private PreparedStatement ps = null; try { conn.setAutoCommit(false); //將自動提交設置爲false ps.executeUpdate("修改SQL"); //執行修改操做 ps.executeQuery("查詢SQL"); //執行查詢操做 conn.commit(); //當兩個操做成功後手動提交 } catch (Exception e) { conn.rollback(); //一旦其中一個操做出錯都將回滾,使兩個操做都不成功 e.printStackTrace(); }
與事務相關的理論
1.事務併發處理可能引發的問題
髒讀(dirty read) 一個事務讀取了另外一個事務還沒有提交的數據,
不可重複讀(non-repeatable read) 一個事務的操做致使另外一個事務先後兩次讀取到不一樣的數據
幻讀(phantom read) 一個事務的操做致使另外一個事務先後兩次查詢的結果數據量不一樣。
舉例:
事務A、B併發執行時,
當A事務update後,B事務select讀取到A還沒有提交的數據,此時A事務rollback,則B讀到的數據是無效的"髒"數據。
當B事務select讀取數據後,A事務update操做更改B事務select到的數據,此時B事務再次讀去該數據,發現先後兩次的數據不同。
當B事務select讀取數據後,A事務insert或delete了一條知足A事務的select條件的記錄,此時B事務再次select,發現查詢到前次不存在的記錄("幻影"),或者前次的某個記錄不見了。
JDBC的事務支持
JDBC對事務的支持體如今三個方面:
1.自動提交模式(Auto-commit mode)
Connection提供了一個auto-commit的屬性來指定事務什麼時候結束。
a.當auto-commit爲true時,當每一個獨立SQL操做的執行完畢,事務當即自動提交,也就是說每一個SQL操做都是一個事務。
一個獨立SQL操做何時算執行完畢,JDBC規範是這樣規定的:
對數據操做語言(DML,如insert,update,delete)和數據定義語言(如create,drop),語句一執行完就視爲執行完畢。
對select語句,當與它關聯的ResultSet對象關閉時,視爲執行完畢。
對存儲過程或其餘返回多個結果的語句,當與它關聯的全部ResultSet對象所有關閉,全部update count(update,delete等語句操做影響的行數)和output parameter(存儲過程的輸出參數)都已經獲取以後,視爲執行完畢。
b. 當auto-commit爲false時,每一個事務都必須顯示調用commit方法進行提交,或者顯示調用rollback方法進行回滾。auto-commit默認爲true。
JDBC提供了5種不一樣的事務隔離級別,在Connection中進行了定義。
2.事務隔離級別(Transaction Isolation Levels)
JDBC定義了五種事務隔離級別:
TRANSACTION_NONE JDBC驅動不支持事務
TRANSACTION_READ_UNCOMMITTED 容許髒讀、不可重複讀和幻讀。
TRANSACTION_READ_COMMITTED 禁止髒讀,但容許不可重複讀和幻讀。
TRANSACTION_REPEATABLE_READ 禁止髒讀和不可重複讀,單運行幻讀。
TRANSACTION_SERIALIZABLE 禁止髒讀、不可重複讀和幻讀。
3.保存點(SavePoint)
JDBC定義了SavePoint接口,提供在一個更細粒度的事務控制機制。當設置了一個保存點後,能夠rollback到該保存點處的狀態,而不是rollback整個事務。
Connection接口的setSavepoint和releaseSavepoint方法能夠設置和釋放保存點。
JDBC規範雖然定義了事務的以上支持行爲,可是各個JDBC驅動,數據庫廠商對事務的支持程度可能各不相同。若是在程序中任意設置,可能得不到想要的效果。爲此,JDBC提供了DatabaseMetaData接口,提供了一系列JDBC特性支持狀況的獲取方法。好比,經過DatabaseMetaData.supportsTransactionIsolationLevel方法能夠判斷對事務隔離級別的支持狀況,經過DatabaseMetaData.supportsSavepoints方法能夠判斷對保存點的支持狀況。
spring提供的事務管理能夠分爲兩類:編程式的和聲明式的。編程式的,比較靈活,可是代碼量大,存在重複的代碼比較多;聲明式的比編程式的更靈活。
spring提供了幾個關於事務處理的類:
TransactionDefinition //事務屬性定義
TranscationStatus //表明了當前的事務,能夠提交,回滾。
PlatformTransactionManager 這個是spring提供的用於管理事務的基礎接口,其下有一個實現的抽象類AbstractPlatformTransactionManager,咱們 使用的事務管理類例如DataSourceTransactionManager等都是這個類的子類。
通常事務定義步驟:
TransactionDefinition td = new TransactionDefinition(); TransactionStatus ts = transactionManager.getTransaction(td); try { //do sth transactionManager.commit(ts); } catch(Exception e){ transactionManager.rollback(ts); }
編程式主要使用transactionTemplate。省略了部分的提交,回滾,一系列的事務對象定義,需注入事務管理對象.
void add(){
transactionTemplate.execute( new TransactionCallback(){
pulic Object doInTransaction(TransactionStatus ts)
{ //do sth}
}
}
聲明式:
使用TransactionProxyFactoryBean:
圍繞Poxy的動態代理 可以自動的提交和回滾事務
org.springframework.transaction.interceptor.TransactionProxyFactoryBean
PROPAGATION_REQUIRED–支持當前事務,若是當前沒有事務,就新建一個事務。這是最多見的選擇。
PROPAGATION_SUPPORTS–支持當前事務,若是當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY–支持當前事務,若是當前沒有事務,就拋出異常。
PROPAGATION_REQUIRES_NEW–新建事務,若是當前存在事務,把當前事務掛起。
PROPAGATION_NOT_SUPPORTED–以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER–以非事務方式執行,若是當前存在事務,則拋出異常。
PROPAGATION_NESTED–若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則進行與PROPAGATION_REQUIRED相似的操做。