JDBC分布式事務淺析
在沒有開始JDBC分布式事務前,先來回顧一下J2EE平臺的數(shù)據(jù)源的整體構加:
在上面的介紹中,可能大家會過于限入理解如何實現(xiàn)ConnectionPoolDataSource和PooledConnection,而忘記了它的整體結構.為了幫助大家更快地理解,我從以下幾點進行總結.
聲明:本文完全是作者根據(jù)SUN的文檔從實踐中總結,沒有參考(事實上目前我還沒有找到這方面的參考)任何文章:
1.DataSource和ConnectionPoolDataSource的關系:
Sun的文檔中只對ConnectionPoolDataSource接口作了一般性規(guī)定,說明它是PooledConnection的工廠,即ConnectionPoolDataSource是傳統(tǒng)的連結池角色,它負責產生物理連結PooledConnection.而PooledConnection又是Connectio的工廠,一個PooledConnection對象負責產生多個Connection對象供應用程序調用.
而DataSource是對上面兩個過程的包裝,在DataSource中不僅要實現(xiàn)傳統(tǒng)連結池ConnectionPoolDataSource來產生物理連結PooledConnection,還要實現(xiàn)通過每個PooledConnection工廠來產生Connection,最后DataSource通過公開方法返回給調用者的是經(jīng)過兩次工廠出來的Connection.如果我們先不考慮JDBC分布式事務,只看下圖左邊,就是說工廠ConnectionPoolDataSource生成PooledConntion,二級工廠PooledConntion生產Connection,這兩個過程由DataSource在內部包裝,只提供最后的產品Connection.
2.DataSource是服務端數(shù)據(jù)源,而傳統(tǒng)的連結池是客戶端數(shù)據(jù)源:
傳統(tǒng)的連結池要調用者生成這個連池的實例,完成初如化,這樣一個數(shù)據(jù)庫為了防止連結池的實例生成無限多個物理連結,就要對保存物理連結的數(shù)據(jù)結構進行靜態(tài)定義,否則,你在你的程序中生成一個連結池對象,它生成30個物理連結,我又在我的程序中生成連結池實例,又生成30個物理連結,那就無法控制了,所以保存這30個物理連結的數(shù)據(jù)結構必須是靜態(tài)的.
而DataSource同一個對象初始化后,對象被綁定到jndi服務器上,通過jdni得到的是它的代碼存根,其中只包含Connection,而物理連結是不可能序列化的,所以不會被重新生成,調用者通過Connection對象作為參數(shù)傳給服務端,由它來操作實際的物理連結.
思考一下:如果不考慮性能問題,我是否可以把PooledConnction不再二次工廠化,只把PooledConntion作為Connection作為DataSource產品返回給調用者?
答案是不可以,因為物理連結不能序列化,也就是無法進行分布式引用.二次工廠化不僅解決了性能的問題,也同時解決了JDBC分布式事物調用的問題.
3.為什么說二次工廠化增加性能?
對于產生物理連結,沒有什么區(qū)別,但物理連結本身并沒有滿負載工作,也就是一個物理Connection(TMD,我現(xiàn)在也不好說Connection還是PooledConnectio,以前的Connection就是DataSource中的PooledConnection)其實可以同時綁定更多的Statement,而如果它直接給調用者調用了,句柄就被調用者拿去了,在調用者沒有返回時別的Statement沒法和它進行”聯(lián)系”.而二次工廠的目的就是把多人的Statement通過”新的引用Connection”和物理連結綁定,使它更好地工作.
舉個例子,汽車這種東西,在目前的中國還是很昂貴的,作為客戶(調用者)我有幾件貨物要運,但一輛汽車(物理連結)如果我一用,別人就不能用了.(傳統(tǒng)連結池和連結),盡管它還可以裝更多的貨物,現(xiàn)在汽車公司只能你一個車號(新的Connection),不給你實際的汽車,你只要把你的幾件東西只交給這個車號,而其他人也可能同時把幾件東西交給這個車號,最盡有更多的貨物因為使用同一車號而使那個物理汽車裝載了更多的貨物,當然如果它滿了的話會產生另一輛車,如果生產的輛達到規(guī)定的數(shù)目你只好等等了,但這樣把多個客戶的貨物和同一車號關聯(lián)使汽車能更多地處理事務,明顯地增加了性能.
理解了以上的結構,我們就不難理解javax.sql對分布式事務的支持,當然,如果你對事務本身還不理解,那我就沒辦法讓你理解以下的知識,因為我不可能再停下來講什么是事務.它是和JDBC相同級別的內容,也許在別的地方我會再講.
從上面的結構中右邊看到,在DataSource中,其實封裝了兩種工廠,這兩種工廠都是兩層次的,其實XADataSource的作用和ConnectioPooledDataSource一樣,都是產生物理連結的,只不過它產生支持分布式事務的物理連結XAConnectio而已,(以后記住,凡以XA命名的類都是支持JDBC分布式事務的標記.)我們看到,XAConnection中getConnection()出來的連結和PooledConnection中getConnection()出來的連結沒有區(qū)別,而Connection是DataSource的最終產品,這意味作什么?
這意味著支持分布式事務的過程由DataSource來做,你要操作的Connection和平時沒有兩樣,你只要聲明事務的開始和事務提交就行了!要使你的連結支持JDBC分布式事務,你要在DataSource的配置中指明type是XADataSource就行了.然后申請一個一務(為了說明方便省略了
- try{}catch(){})
- UserTransaction ut = ...........;
- ut.begin();
- Connection con1 = .........;
- Connection con2 = .........;
- Connection con3 = .........;
- if(條件) ut.setRollbackOnly();
- con1.close();
- con2.close();
- con3.close();
考察一下,為什么XADataSource類型的物理工廠會產生的連結可以直接被事務管理呢?其實這就是封裝的好處了,在XADataSource產生XAConnection時,這個XAConnection實際是PooledConnection的子類,它擴展了一個getXAResource() 方法,事務通過這個方法把它加入到事務容器中進行管理.對于調用者來說,根本看不到事務是如果管理的,你只要聲明開始事務,告訴容器我下面的操作要求事務參與了,最后告訴事務說到這兒可以提交或回滾了,別的都是黑箱操作,不要你來做.
當然如果沒有分布式事務的需求,雖然XADataSource可以用于本地事務,但它要做很多資源測試,是一種浪費.
最后要說明的是,既然你把操作交給事務來做,你就要對他放心,事務邊界由容器管理,你只在最后確定是提交還是回滾還是強行回滾setRollbackOnly()(強行回滾后不可以再提交).你不要在事務中調用某一連結的rollback,commit,也不能把Connection設為自動提交,一般來說當你聲明為支持JDBC分布式事務的DataSource時,創(chuàng)建的連結默認都是關閉自動提交的,只是你自己不要打開它.
因為SUN的文檔只對DataSource接口作了一般規(guī)定,并沒有規(guī)定具體算法,所以我們在清楚上面的結構后,可以實現(xiàn)不依賴容器的DataSource(其實只是它的思想.因為你寫出來的不依賴容器的DataSource)已經(jīng)不是這個意義上的DataSource了.它不能綁定到服務器上讓遠程引用,所以生成物理連結的工廠應該是靜態(tài)的,而物理連結這種產品也應該是靜態(tài)的.然后再生成多個引用連結.但這好象沒有多大意義,因為純客戶端軟件一般來說不可能同時有上萬個客戶在線訪問的,根本用不著這么費事地實現(xiàn)連結池.
【編輯推薦】