什麼是事務?
在計算機術語中是指訪問數據庫,並對數據進行操作,完成單一邏輯功能的一個程序執行單元(unit)。
通常的概念認爲,事務僅與數據庫相關。
事務必須服從 ISO/IEC 制定的ACID原則。ACID是指 atomicity (原子性)、consistency(一致性)、isolation(隔離性)、durability(持久性)。
1):原子性(atomicity):事務是數據庫的邏輯工作單元,而且必須是原子工作單元,對於數據的修改,要麼全部執行,要麼全部不執行。
舉例:
在銀行系統中,有兩個儲蓄賬戶A和B,A賬戶中有 3000 元人民幣,B賬戶中有 1000 元人民幣。
現在,A想要從自己的賬戶中轉 1000 元到B的賬戶中,那麼從A開始轉賬,到轉賬結束的這一過程,稱之爲一個事務。
在這個事務中,需要做的操作如下:
①:從A的賬戶中減去 1000 元。A賬戶中原有 3000 元,減去 1000 元后應該剩餘 2000 元。
②:在B的賬戶中增加 1000 元。B賬戶中原有 1000 原,增加 1000 元后應該剩餘 2000 元。
如果在A的賬戶已經減去 1000 元的時候,忽然發生了意外,比如銀行轉賬系統出現問題,導致轉賬事務意外終止,而此時,B的賬戶中還沒有增加 1000 元,那麼,我們認爲這個操作失敗了,事務需要回滾,回滾就是需要回到事務執行之前的狀態,也就是要回到A賬戶 3000 元餘額,B賬戶 1000 元餘額的時候。
我們把要麼一起成功(A賬戶減去 1000 元,同時B賬戶相應增加 1000 元),要麼一起失敗(A賬戶回到原來狀態,B賬戶同樣回到原來狀態)的操作叫做事務的原子性操作。
2):一致性(consistency):事務在完成時,必須是所有的數據都保持一致的狀態。在相關數據庫中,所有的規則都必須應用於事務的修改,以保持數據的完整性。
舉例:轉賬操作中,兩個賬戶A\B的餘額相加,總額不變。
賬戶A要給賬戶B轉賬 1000 元。事務要做的是從A的賬戶減去 1000 元,相應的在B的賬戶中增加 1000 元。一致性的含義是,其他事務要麼看到的是A還沒有給B轉賬的狀態,要麼看到的是A已經成功給B轉賬的狀態,而對於A賬戶已減去 1000 元,B賬戶還沒有增加 1000 元的這個中間狀態,其他事務是不可見的。
3):隔離性(isolation):一個事務的執行,不能被其他事務所影響。
4):持久性(durability):一個事務一旦提交,事務對數據的操作便永久性的保存在DB中。即便是在數據庫系統遇到故障的情況下,也不會丟失事務的操作。
JAVA事務的類型
JAVA事務的類型有3種:JDBC事務、JTA(java transaction api)事務、容器事務。
①:JDBC事務
在JDBC中處理事務,都是通過Connection對象進行事務管理。同一事務中所有的操作,都在使用同一個Connection對象。JDBC事務默認是開啓的,並且是默認提交。
JDBC Connection 接口提供了兩種事務模式:自動提交和手工提交。
常用的事物相關方法是:setAutoCommit\commit\rollback等。
setAutoCommit(boolean):設置是否爲自動提交事務,如果true(默認值爲true)表示自動提交,也就是每條執行的SQL語句都是一個單獨的事務,如果設置爲false,需要手動提交事務。
commit():提交結束事務。
rollback():回滾結束事務。
JDBC事務的優缺點:
JDBC爲使用Java進行數據庫的事務操作提供了最基本的支持。
通過JDBC事務,我們可以將多個SQL語句放到同一個事務中,保證其ACID特性。
JDBC事務的主要優點就是API比較簡單,可以實現最基本的事務操作,性能也相對較好。
不支持多數據庫的事務,一個 JDBC 事務不能跨越多個數據庫。所以,如果涉及到多數據庫的操作或者分佈式場景,JDBC事務就無能爲力了。
簡單的JDBC示例:
public void JdbcTransfer() {
java.sql.Connection conn = null;
Statement stmt = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
conn = DriverManager.getConnection( "jdbc:oracle:thin:@localhost:1521:orcl", "username", "password");
// 將自動提交設置爲 false,
// 若設置爲 true 則數據庫將會把每一次數據更新認定爲一個事務並自動提交
conn.setAutoCommit(false);
stmt = conn.createStatement();
// 將 A 賬戶中的金額減少 1000
stmt.execute("update t_account set amount = amount - 1000 where account_id = 'A'");
// 將 B 賬戶中的金額增加 1000
stmt.execute("update t_account set amount = amount + 1000 where account_id = 'B'");
// 提交事務
conn.commit();
// 事務提交(轉賬的兩步操作同時成功)
} catch (SQLException e) {
try {
// 發生異常,回滾本事務中的操做
conn.rollback();
// 事務回滾:轉賬的兩步操作完全撤銷
stmt.close();
conn.close();
} catch (Exception ignore) {
}
e.printStackTrace();
}
}
②:Java transaction api (JTA)事務
Java transaction api (Java事務API)和Java transaction service (Java事務服務)共同爲J2EE平臺提供了分佈式事務管理。JTA只是單純的接口,沒有事務的具體實現,需要J2EE服務提供商根據JTS規範提供。
常見的JTA實現方式有:J2EE容器提供的實現(如 JBOSS)、獨立的JTA實現(如JOTM、Atomikos),這些實現可以應用在那些不適用J2EE應用服務器的環境裏,以提供分佈式事務保證(如Tomcat、Jetty以及普通的java應用)。
JTA提供了 java.transaction.UserTransaction接口,裏面定義了下面的方法:
begin:開啓一個事務。
commit:提交一個事務
rollback:回滾一個事務。
setRollBackOnly:把當前事務標記爲回滾。
setTransactionTimeout:設置事務的時間,超過這個時間,就拋出異常,回滾事務。
值得注意的是,並不是實現了UserTransaction接口就能把普通的JDBC操作直接轉成JTA操作,JTA對DataSource、Connection與Resource都是有要求的,只有符合XA規範,並且實現了XA規範的相關接口的類才能參與到JTA事務中來。(PS:主流的數據庫都支持XA規範)。
JTA的優缺點:
JTA的優點很明顯,就是提供了分佈式事務的解決方案,嚴格的ACID。
JTA的缺點是實現複雜。
JTA本身就是個笨重的API,通常JTA只能在應用服務器環境下使用,因此使用JTA會限制代碼的複用性。
簡單示例:
public void JtaTransfer() {
javax.transaction.UserTransaction tx = null;
java.sql.Connection conn = null;
Statement stmt = null;
try {
// 取得JTA事務,本例中是由Jboss容器管理
tx = (javax.transaction.UserTransaction) context.lookup("java:comp/UserTransaction");
// 取得數據庫連接池,必須有支持XA的數據庫、驅動程序
javax.sql.DataSource ds = (javax.sql.DataSource) context .lookup("java:/XAOracleDS");
tx.begin();
conn = ds.getConnection();
// 將自動提交設置爲 false,
// 若設置爲 true 則數據庫將會把每一次數據更新認定爲一個事務並自動提交
conn.setAutoCommit(false);
stmt = conn.createStatement();
// 將 A 賬戶中的金額減少 1000
stmt.execute("update t_account set amount = amount - 1000 where account_id = 'A'");
// 將 B 賬戶中的金額增加 1000
stmt.execute("update t_account set amount = amount + 1000 where account_id = 'B'");
// 提交事務
tx.commit();
// 事務提交:轉賬的兩步操作同時成功
} catch (SQLException sqle) {
try {
// 發生異常,回滾在本事務中的操做
tx.rollback();
// 事務回滾:轉賬的兩步操作完全撤銷
stmt.close();
conn.close();
} catch (Exception ignore) {
}
sqle.printStackTrace();
}
}
③:容器事務
容器事務主要是J2EE應用服務提供的,容器事務大多基於JTA實現,是基於JNDI的。
spring 容器事務
spring並不直接管理事務,而是提供了一致的事務模型。不管是使用 JDBC、hibernate或者是使用mybatis來操作數據,也不管使用的是DataSource事務或者JTA事務,spring將事務管理的職責委託給例如 JDBC、hibernate或JTA等持久化機制所提供的框架來處理事務。
spring事務抽象管理接口:
spring 事務抽象管理接口 org.springframework.transaction.PlatformTransactionManager 爲各種平臺(如:JDBC\hibernate\JTA)提供了對應的事務管理器,對事務的具體實現,將交由各平臺。
例如,如果使用DataSource,我們可以配置DataSourceTransactionManager;如果使用 hibernate ,我們可以配置 HibernateTransactionManager;使用JTA的話則可以配置 JtaTransactionManager。
Public interface PlatformTransactionManager{
// 由TransactionDefinition得到TransactionStatus對象
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交
Void commit(TransactionStatus status) throws TransactionException;
// 回滾
Void rollback(TransactionStatus status) throws TransactionException;
}
由以上接口我們可以看到,事務管理器接口PlatformTransactionManager通過getTransaction(TransactionDefinition definition)方法來得到事務,參數是TransactionDefinition類對象,這個參數類中定義了一些基本的事務屬性(事務屬性可以理解成事務的一些基本配置,描述了事務策略如何應用到方法上)。
public interface TransactionDefinition {
int getPropagationBehavior(); // 返回事務的傳播特性
int getIsolationLevel(); // 返回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些數據
int getTimeout(); // 返回事務必須在多少秒內完成
boolean isReadOnly(); // 事務是否只讀,事務管理器能夠根據這個返回值進行優化,確保事務是隻讀的
}
事務屬性包含:傳播特性、隔離級別、回滾規則、事務超時、是否只讀7種傳播特性:
PROPAGATION_REQUIRED:(required) 如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。( 當前有事務就用當前,沒有就用新的。)
PROPAGATION_SUPPORTS:(supports) 支持當前事務,如果當前沒有事務,就以非事務方式執行。(事務可有可無,不是必須的。)
PROPAGATION_MANDATORY:(mandatory) 支持當前事務,如果當前沒有事務,就拋出異常。(當前一定要有事務,不然就拋出異常。)
PROPAGATION_REQUIRES_NEW:(required_new) 新建事務,如果當前存在事務,把當前事務掛起。(無論當前是否有事務,都起一個新的事務。)
PROPAGATION_NOT_SUPPORTED:(not_supports) 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。(不支持事務,按非事務方式運行。)
PROPAGATION_NEVER:(never) 以非事務方式執行,如果當前存在事務,則拋出異常。 (不支持事務,如果有事務則拋出異常。)
PROPAGATION_NESTED:(nested) 當前有事務就在當前事務內再起一個事務。
雖然有7種,但是常用的就第一種REQUIRED和第四種REQUIRES_NEW。
五個隔離級別:
ISOLATION_DEFAULT:(default) 默認的隔離級別,使用數據庫默認的事務隔離級別。
ISOLATION_READ_UNCOMMITTED:未提交讀 (read_uncommitted) 事務最低的隔離級別,它充許另外一個事務可以看到這個事務未提交的數據。
ISOLATION_READ_COMMITTED:讀取已提交(read_committed)保證一個事務修改的數據提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。
ISOLATION_REPEATABLE_READ:可重複讀(repeatable_read) 保證一個事務不能讀取另一個事務未提交的數據外。
ISOLATION_SERIALIZABLE:連續(serializable)事務被處理爲順序執行(花費最高代價但是最可靠)。