【JavaEE】經典JAVA EE企業應用實戰-讀書筆記1
今天開始閱讀李剛的書《經典JAVA EE企業應用實戰 基於WebLogic JBoss的JSF+EJB3+JPA整合開發》這本書,
並記錄下讀書筆記,以方便以後參考。
Java EE應用大致可分為如下幾層:
1,Domain Object(領域物件)層:此層由系列的POJO組成。
2,DAO(Data Access Object,資料訪問物件)層
3,業務邏輯層
4,控制器層
5,表現層
JBoss是一款很著名的開源的JavaEE應用伺服器
事務控制也是Java EE應用中必須要處理的問題,他可以保證一系列資料庫操作能夠準確完成。事務及時保證底層資料庫完整性的重要手段,也是保證應用業務邏輯成功執行的重要基礎。對於一個實際企業級應用而言,既需要區域性事務控制,也需要全域性事務控制。
JTA(Java Transaction API)則提供了事務劃分的標準介面,尤其是當應用程式執行兩個需要依賴於不同資料庫的操作時,應用程式就需要使用JTA來將兩個操作包含成一個全域性事務。
事務是由一步或幾步資料庫操作序列組成的邏輯執行單元,這系列操作要麼全部執行,要麼全部放棄執行。一段程式中可能包含多個事務。
事務具備4個特性:原子性Atomicity,一致性Consistency,隔離性Isolation和持續性Durability,簡稱ACID特性。
原子性:事務是應用中最小執行單位,事務是應用中不可再分的最小邏輯執行體。
一致性:事務執行的結果,必須使資料庫從一個一致狀態變到另一個一致性狀態。一致性是通過原子性來保證的。
隔離性:各個事務的執行互不干擾,任意一個事務的內部操作對其他併發的事務都是隔離的。也就是說,併發執行的事務之間不能看到對方的中間狀態,併發執行的事務之間也不能互相影響。
持續性:也稱為永續性,指事務一旦提交,對資料所做的任何改變都要記錄到永久儲存器中,通常就是儲存進物理資料庫。
Java EE應用中的事務處理一般可以分為如下兩類:
區域性事務,Local Transaction Processiong
分散式事務,Distributed Transaction Processiong,DTP
對於絕大部分企業應用而言,它只需要涉及單一的資料庫資源,因此只需要採用區域性事務即可。區域性事務通常採用單階段提交;但是也有一些更復雜的企業級應用採用分散式事務處理。分散式事務處理的關鍵是必須有一種方法保證多個數據庫所做的全部動作,他們也可以作為一個整體,這樣才能保證黨業務邏輯跨越多個數據庫資源時讓多個數據庫的資料保持一致。
必須藉助於中介軟體的事務管理器來進行協調,中介軟體的事務管理器負責通知和協調所有參與事務的相關資料庫的提交或回滾,最後由各個單獨的資料庫將自己所做的操作進行實質提交或回滾。
從應用程式的角度來看,中介軟體的事務管理器消除了底層事務處理的複雜性,從而簡化了分散式事務處理的程式設計步驟——應用程式無需理會底層多個事務資源區域性事務的開始、提交或回滾操作,應用程式只要面向中介軟體的事務管理器程式設計,用全域性事務管理的API來開始、提交或回滾事務即可,而底層則可能轉換為多個低級別的事務命令。
當一個業務操作需要涉及多個數據庫時,就可稱為分散式事務處理了。為了協調多個事務資源的分散式事務處理,多個事務資源底層必須使用一種通用的事務協議,目前流行的分散式事務處理規範就是XA規範。
X/Open組織(即現在的Open Group)定義了分散式事務處理模型。X/Open DTP模型(1994)包括應用程式AP、事務管理器TM、資源管理器RM、通訊資源管理器CRM,一共4個部分。一般來說,常見的事務管理器TM就是事務中介軟體(通常由應用伺服器來實現),常見的資源管理器是資料庫,常見的通訊資源管理器是訊息中介軟體。
X/Open組織為分散式事務處理指定了事務中介軟體與資料庫之間的介面規範,這種規範就是XA規範。事務中介軟體用它來通知資料庫事務的開始、提交或回滾等。
該組織僅僅指定了分散式事務處理的XA規範,但是具體的實現則由不同的資料庫廠商自行提供。
XA規範的理論基礎就是兩階段提交(2 Phase Commit,2PC)協議,該協議定義了單個的事務管理器如何協調和管理一個或多個數據庫的區域性事務,該協議大致可分為如下5個步驟:
1,應用程式面向事務管理器程式設計,應用程式呼叫事務管理器的提交方法。
2,事務管理器通知參與全域性事務的每個資料庫,告訴他們準備開始提交事務——第一個階段開始。
3,參與全域性事務的各個資料庫進行區域性事務的預提交。
4,事務管理器收集到各個資料庫預提交的結果。
5,第二階段開始,事務管理器收集到所有參與全域性事務預提交的結果之後做出相應的判斷:如果所有資料庫的區域性事務預提交結果都可以成功,事務管理器向每個資料庫都發送進行實際提交的命令;如果任意一個數據庫的區域性事務預提交的結果失敗了,事務管理器將向每個資料庫傳送進行實際回滾的命令,讓所有資料庫退回修改之前的狀態。
對於單個數據庫區域性事務預提交的過程,進一步解釋:當某一個數據庫收到預提交要求後,如果可以提交屬於自己的事務分支,則將自己在該事務分支中所做的操作記錄下來,並給事務中介軟體一個同意提交的應答,此時資料庫將不能再想該事務分支中加入任何操作,但此時資料庫並沒有真正提交改事務,底層資料庫對共享資源的操作還未釋放(處於上鎖狀態)。如果由於某種原因資料庫無法提交屬於自己的事務分支,將回滾自己的所有操作,釋放對共享資源上的鎖,並返回給事務中介軟體失敗的應答。
在典型的Java EE應用伺服器中,全域性事務管理器是必須的元件。它會負責協調和管理參與全域性事務的多個數據庫的區域性事務處理。
在一個涉及多個數據庫的分散式事務處理中,為保證全域性事務的完整性,兩階段提交時必須的。但是典型的兩階段提交,對資料庫來說事務從開始到結束時間相對較長,在事務處理期間資料庫使用的資源將一直處於鎖定狀態,直到事務結束才會釋放。因此,使用典型的兩階段提交相對來說會佔用更多的資源,因此會帶來一定的效能損失。
通過使用JTA程式設計,開發者可以用一種與事務管理器無關的方式處理事務,Java EE應用伺服器通過Java事務服務(Java Transaction Service,JTS)來實現事務管理器。但應用程式程式碼無需直接呼叫JTS方法,只需要面向JTA方法即可,由JTA來呼叫底層的JTS進行處理。
如果希望使用JTA事務,可呼叫javax.transaction.UserTransaction介面的begin、commit、rollback等方法來控制事務。
Context ctx = new InitialContext();
DataSource oracleDs = (DataSource) ctx.lookup("oracle");
DataSource otherDs = (DataSource) ctx.lookup("other");
UserTransaction tx = (UserTransaction) ctx.lookup("javax.transaction.UserTransaction");
Connection oracleConn = oracleDs.getConnection();
Connection otherConn = otherDs.getConnection();
Statement oracleStmt = null;
Statement otherStmt = null;
tx.begin();
try {
oracleStmt = oracleConn.createStatement();
otherStmt = otherConn.createStatement();
int result = oracleStmt.executeUpdate("insert into dept values(50,'研發部','廣州')");
System.out.println(result == 1 ? "插入成功" : "插入失敗");
otherStmt.executeUpdate("insert into dept values(40,'市場部','廣州')");
tx.commit();
} catch (SQLException e) {
e.printStackTrace();
tx.rollback();
} finally {
oracleStmt.close();
otherStmt.close();
oracleConn.close();
otherConn.close();
}
上面的程式碼是一個典型的JTA事務控制邏輯,適用於WebLogic。
jboss中如果在Resource->Datasources->XA Datasources中配置後,在server\default\deploy目錄下就會生成一個xxx-ds.xml的檔案。
DataSource oracleDs = (DataSource) ctx.lookup("java:/oracle");
DataSource otherDs = (DataSource) ctx.lookup("java:/other");
//適用於jboss
UserTransaction tx = (UserTransaction) ctx.lookup("UserTransaction");
事務控制是保證應用程式底層資料完整性的重要手段,但事務控制對程式效能的影響也是很大的,如果應用程式的事務屬性設定不合適,應用程式的效能將會大大降低。
事務隔離指的是事務處理中如何管理事務的隔離性,所謂隔離性實際上就意味著資料庫如何處理併發操作的問題,當兩個或者多個操作處理同一個資料時,可能引起訪問的衝突和資料庫的不一致。
根據JDBC規範的定義,事務的隔離設定一共分為5種級別,這5中級別的詳細規定依賴於具體使用的資料庫。下面是簡單的介紹:
TRANSACTION_NONE:對事務和資料不進行任何隔離限制。
TRANSACTION_READ_UNCOMMITTED:這種隔離級別允許事務讀取另一個事務的未提交資料。舉例來說,A執行緒將某個賬戶的餘額減少了1000元,即使在事務A未提交之前,當事務B試圖讀取該賬戶餘額時,也將讀到減少後的賬戶餘額。在這種隔離級別下,儘管事務A沒有提交,但事務B讀到的總是最新的資料。但是這種隔離級別有一個很嚴重的問題:讀取髒資料,對於前面介紹的情形,當事務B讀取到未提交的賬戶餘額之後,如果事務A又回滾了,那意味著B讀的資料是不正確的。
TRANSACTION_READ_COMMITTED:這種隔離級別保證所有讀到的資料都是已經提交的資料,這樣可以避免第二種情況的出現,但他不允許重複讀。舉例來說,當事務A開始讀到某條記錄的餘額為2000元,在事務A未提交之前,事務B把這條記錄的賬戶餘額改為1000元,並提交了事務B,當事務A再次讀取時將讀到1000元,這就是不可重複讀。因為對於同一條資料記錄,事務A讀取了兩次,但兩次讀到的結果並不相同。對於Oracle而言,TRANSACTION_READ_COMMITTED是預設的隔離級別,在應用中通常應採用這種隔離設定。
TRANSACTION_REPEATABLE_READ:這種隔離級別可以防止第二、第三種情形的出現,他是一種可重複讀的事務隔離。但當其他關聯表中的資料行被插入或刪除時,讀事務依然可能出現一種邊界效應,從而造成某種程度的錯覺。假設現在資料庫中有兩個表:表一記錄當前全部學生的詳細記錄(假設每條記錄代表一個學生);表二則記錄了表一的統計資訊,包括學生總人數、男生人數、女生人數等。事務A讀取表一記錄,並計算出學生總人數、男生人數、女生人數等統計資訊,接下來事務B向表一中插入記錄並修改表二的統計記錄,如果此時恰好事務A去讀取表二中的統計資訊,並用讀到的統計資訊與之前通過表一計算出來的統計資訊進行對比,將會發現兩組統計資訊並不相等(新讀到的統計資料更大)。
TRANSACTION_SERIALIZABLE:這種隔離設定可以防止第二、第三、第四種情形的出現,其代表真正可序列化的事務,提供了最高級別的保護,但是這種事務方式的效能也是最低階的。
通過Connection介面定義的setTransactionIsolation()方法可以設定隔離級別,Connection介面中定義了上面的5個常量。
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
由於事務隔離級別需要底層資料庫的支援,但並不是每個資料庫都支援所有的隔離級別,不同資料庫也可能採用不同的鎖定演算法,因此設定時還需要查閱資料庫的相關資料。