1.概述:
在jdbc的數據庫操作中,一項事務是由一條或是多條表達式所組成的一個不可分割的工作單元。我們通過提交commit()或是回滾rollback()來結束事務的操作。關于事務操作的方法都位于接口java.sql.Connection中。
2.特點:
★ 在jdbc中,事務操作缺省是自動提交。也就是說,一條對數據庫的更新表達式代表一項事務操作,操作成功后,系統將自動調用commit()來提交,否則將調用rollback()來回滾。
★ 在jdbc 中,可以通過調用setAutoCommit(false)來禁止自動提交。之后就可以把多個數據庫操作的表達式作為一個事務,在操作完成后調用 commit()來進行整體提交,倘若其中一個表達式操作失敗,都不會執行到commit(),并且將產生響應的異常;此時就可以在異常捕獲時調用 rollback()進行回滾。這樣做可以保持多次更新操作后,相關數據的一致性,示例如下:
try {
conn =
DriverManager.getConnection
("jdbc:oracle:thin:@host:1521:SID","username","userpwd";
conn.setAutoCommit(false);//禁止自動提交,設置回滾點
stmt = conn.createStatement();
stmt.executeUpdate(“alter table …”); //數據庫更新操作1
stmt.executeUpdate(“insert into table …”); //數據庫更新操作2
conn.commit(); //事務提交
}catch(Exception ex) {
ex.printStackTrace();
try {
conn.rollback(); //操作不成功則回滾
}catch(Exception e) {
e.printStackTrace();
}
}
★ jdbc API支持事務對數據庫的加鎖,并且提供了5種操作支持,2種加鎖密度。
5種支持:
static int TRANSACTION_NONE = 0;
→禁止事務操作和加鎖。
static int TRANSACTION_READ_UNCOMMITTED = 1;
→允許臟數據讀寫(dirty reads)、重復讀寫(repeatable reads)和影象讀寫(phntom
reads)
static int TRANSACTION_READ_COMMITTED = 2;
→禁止臟數據讀寫(dirty reads),允許重復讀寫(repeatable reads)和影象讀寫(phntom reads)
static int TRANSACTION_REPEATABLE_READ = 4;
→禁止臟數據讀寫(dirty reads)和重復讀寫(repeatable reads),允許影象讀寫(phntom reads)
static int TRANSACTION_SERIALIZABLE = 8;
→禁止臟數據讀寫(dirty reads)、重復讀寫(repeatable reads)和允許影象讀寫(phntom reads)
2種密度:
最后一項為表加鎖,其余3~4項為行加鎖。
臟數據讀寫(dirty reads):當一個事務修改了某一數據行的值而未提交時,另一事務讀取了此行值。倘若前一事務發生了回滾,則后一事務將得到一個無效的值(臟數據)。
重復讀寫(repeatable reads):當一個事務在讀取某一數據行時,另一事務同時在修改此數據行。則前一事務在重復讀取此行時將得到一個不一致的值。
影象讀寫(phantomreads):當一個事務在某一表中進行數據查詢時,另一事務恰好插入了滿足了查詢條件的數據行。則前一事務在重復讀取滿足條件的值時,將得到一個額外的“影象“值。
Jdbc根據數據庫提供的缺省值來設置事務支持及其加鎖,當然,也可以手工設置:
setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);
可以查看數據庫的當前設置:
getTransactionIsolation()
需要注意的是,在進行受動設置時,數據庫及其驅動程序必須得支持相應的事務操作操作才行。
上述設置隨著值的增加,其事務的獨立性增加,更能有效的防止事務操作之間的沖突;同時也增加了加鎖的開銷,降低了用戶之間訪問數據庫的并發性,程序的運行效率也回隨之降低。因此得平衡程序運行效率和數據一致性之間的沖突。一般來說,對于只涉及到數據庫的查詢操作時,可以采用TRANSACTION_READ_UNCOMMITTED 方式;對于數據查詢遠多于更新的操作,可以采用TRANSACTION_READ_COMMITTED方式;對于更新操作較多的,可以采用 TRANSACTION_REPEATABLE_READ;在數據一致性要求更高的場合再考慮最后一項,由于涉及到表加鎖,因此會對程序運行效率產生較大的影響。
另外,在oracle中數據庫驅動對事務處理的缺省值是TRANSACTION_NONE,即不支持事務操作,所以需要在程序中手動進行設置。
3.小結
jdbc提供的對數據庫事務操作的支持是比較完整的,通過事務操作可以提高程序的運行效率,保持數據的一致性。
提示4:正確地使用事務
在創建可靠的系統時,正確地使用事務是非常關鍵的:如果登記一項銷售額涉及到改變三個獨立的表,那么數據庫應該在這三個表中反映出這些變化或者所有表中都不反映。在Oracle中,通過執行一系列SQL 語句來處理事務,然后把它們“提交”到數據庫,或者“回滾”到事務開始。高效的事務設計通常是系統成功部署的關鍵。
核心的JDBC包支持4種“transaction isolation modes(事務隔離模式)”,允許程序指定它們希望的事務行為。Oracle支持它們中的兩種:默認的 “read committed(讀提交數據)” 和更安全的“serializable(可串行化)”模式。“read committed” 模式支持不可重復讀(non-repeatable read),在這種情況下,一個事務所做的修改對查詢中的另一個事務是可見的;還支持錯誤讀取(phantom read),這種情況下,被其他事務所做的修改對于運行的查詢是可見的。依賴于在多個操作中數據庫的完全一致視圖的應用程序應該選擇損失一定的性能,使用更嚴格的“serializable(可串行化)”設置。
JDBC連接在默認的情況下也在“auto-commit”(自動提交)模式下運行,這意味著每個語句一執行完就立即提交給數據庫后。這種模式有一些優點,開發人員不需要在每次更新或插入操作之后調用commit() 方法。它還可以確保最新的數據庫視圖。另一方面,關閉自動提交將可以稍微提高一點性能,并且將允許多語句事務。
在servlet環境中使用JDBC事務時,與使用連接共享時應用了許多相同的規則。在代碼的結尾處或者回滾或者提交事務是非常重要的。確保這一點的最好方法是使用try...catch...finally 塊。不完整的事務可能產生不可預見的后果,包括從不完整或不一致的數據庫視圖到當事務一個接一個排隊等候訪問被鎖定的資源時使整個應用程序被鎖定的種種情況。
下面的例子說明如何使用JDBC事務:
Connection con = null;
try {
ds = (DataSource)myContext.lookup("jdbc/oracleServer");
pooledCon = ds.getConnection("scott", "tiger");
pooledCon.setAutoCommit(false);
pooledCon.setTransactionIsolation(
Connection.TRANSACTION_SERIALIZABLE);
// ..
pooledCon.commit();
} catch (Exception ignored) {
try { pooledCon.rollback(); } catch (SQLException ig) {}
} finally {
if(pooledCon != null) {
pooledCon.setAutoCommit(true);
pooledCon.close();
}
}
連接池中得到的每一個連接是極為重要的。
盡管常規事務很有吸引力,但無論你使用哪種連接管理模型,都絕不應該將它們推廣應用到對服務器的多個請求(一個或更多servlet的多個調用)。用戶決不應該在一個事務沒有完成時就發出第二個請求。需要跨多個對象或多個請求中分布事務的應用程序應該考慮其他可選的體系結構。舉例來說,同Java事務處理API (JTA)一起的JDBC 2.0可選包,使與完善的事務處理服務器的集成成為可能。
如果你正在運行Oracle9i第2版,你將可以獲取JDBC 3.0中引入的一些新的事務特性。這些特性中最重要的是事務存儲點(Transaction Savepoint),它允許你將事務回滾到某一特定點,而不是要么全部回滾、要么全不回滾。
public void createA(Connection conn,A a){
int flag=0;
if(conn==null){
flag=1;
conn = getConnection();
}
try{
{do update}
if(flag==1){
conn.commit();
}
} catch(Exception e){
if(flag==1){
conn.rollback();
}
throw e;
} finally{
if(flag==1){
conn.close();
}
}
}
public void createB(B b){
Connection conn = getConnection();
{create b}
try{
createA(conn,a);
conn.commit();
} catch(Exception e){
conn.rollback();
}