程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java事務開發的常見問題

Java事務開發的常見問題

編輯:關於JAVA

一、了解事務源

我在面試中經常會問到這樣的一個問題,假如有一個全局變量,在一個事務中修改了這個變量的值,而後這個事務因為別的原因回滾了,那這個變量的值會回滾到更改之前的值麼?

其實事務只能對它所管理的資源進行提交和回滾,這些資源就是事務源,它通常包括數據庫連接資源,JMS隊列資源等。事務的ACID(原子性,一致性,隔離性,持久性)屬性也是針對它所管理的資源而言的。前面問題中的一個全局變量,可以說是內存中的一塊存儲空間,那麼內存中的數據如何能具備事務屬性中的持久性呢?很顯然它不在事務的管轄范圍之內,也就不會跟著事務的回滾而回滾了。

二、何時回滾事務

在JDBC事務和EJB的Bean管理事務中,我們通常會按下面這種方式控制事務的回滾。在出現某種的異常情況下,我們可以控制讓事務回滾,當然也可以提交這個事務。

Connection cn = ...
cn.setAutoCommit(false);
Statement stmt = cn.createStatement();
try{
stmt.executeUpdate("update Order...");
cn.commit();
}catch(Exception e) {
cn.rollback();  //出現異常,回滾當前事務
}finally{
stmt.close();
cn.close();
}

但是對於EJB的容器管理事務或者Spring的聲明式事務,就不大一樣了。例如:

Connection cn = ...
cn.setAutoCommit(false);
Statement stmt = cn.createStatement();
try{
stmt.executeUpdate("update Order...");
cn.commit();
}catch(Exception e) {
cn.rollback();  //出現異常,回滾當前事務
}finally{
stmt.close();
cn.close();
}

我們只告訴EJB容器這個方法需要事務控制,容器會在方法的開始處啟動一個事務,在方法返回之前提交這個事務。那如果中間處理過程中出現異常該怎麼辦?默認情況下只有在RuntimeException和標注為ApplicationException的異常發成時會回滾事務,而在其他的情況下,事務都會提交。也就是說,在異常發生之前所有的操作都會被提交。這就有可能會出現部分提交的問題。有時為了確保一個方法所有的操作都被提交或者回滾,通常會這樣做:

@TransactionAttribute(TransactionAttributeType.Required)
public void updateOrder(Order order) {
try{
...
}catch(Exception e) {
throw new EJBException(e);
}
}

在有異常發生時,捕獲這個異常,並把它包裝成一個EJBException,並重新拋出它。因為EJBException繼承了RuntimeException,所以這裡拋出一個EJBException告訴EJB容器回滾當前的事務。

另外需要注意的是另外一個方法:setRollbackOnly(),它與EJBException不同的是,它僅僅標記當前事務需要回滾,在方法執行完成之後容器會檢查它並回滾事務,它並不拋出任何異常。

相比較EJB,Spring的聲明式事務好像控制的更細致一些。來看下面的例子。

@Transactional(rollbackFor={Exception.class})
public void updateOrder(Order order) {
...
}

我們可以告訴Spring哪種類型的異常需要回滾,當然默認的還是只有在發生RuntimeException時事務會回滾。如果大家也在使用這種事務控制方式的話,還是主動告訴容器何時回滾,何時提交事務吧,采用默認值並不是一個好的辦法。

三、事務的范圍

以前的一個項目是直接使用JDBC來訪問數據庫的,後來決定重構到Hibernate和Spring上去,事務控制也由原來的本地JDBC事務轉換為Spring的聲明式事務。好像一切都很順利,直到交給性能測試人員。

造成性能低下的原因也很明顯,就是由於使用了聲明式事務,事務的控制范圍是從一個方法的開始到它的結束。如果這個方法很長,而只需要確保其中的某幾條數據庫的操作語句在一個事務中執行,這樣,本來只需要一個很短的事務,結果卻使用了一個很長的事務。事務一旦過長,它就會影響別的進程或線程來操作同一個事務源。合理的方法應該是在確實需要事務的時候才開始一個事務,而且要及時提交或回滾它,以釋放對事務源的訪問,這也就是EJB中,使用BMP(Bean管理事務)相對與CMP(容器管理事務)的好處。如果確定需要在Spring中使用類似BMP的做法,也就是說開發人員自己控制事務,可以通過Spring容器注入Spring的TransactionManager來實現,當然這也就違背了“無浸入”的原則,需要大家在實際中權衡。

另外Spring的事務標注還給我們提供了一些其他的屬性,比如說readOnly,isolation和timeout等。當一個方法需要添加事務控制,而且這個方法只做查詢操作的時候,千萬別忘記標記“readOnly = true”,這樣這個事務就可以和別的事務同時訪問事務源了。

四、分布式事務

一提到分布式事務,大家可能都想到有多個數據庫分布在不同的計算機上。其實,不僅如此,例如一個事務需要同時控制對本地的數據庫操作和本地的JMS隊列,也可以稱為分布式事務。在EJB容器中,例如JBoss,我們會配置一些數據源,一些JMS隊列等,無論采用BMP(Bean管理事務)還是CMP(容器管理事務),我們在真正使用的是JTA(Java Transaction API)的UserTransaction,它就是一個分布式事務,它可以控制容器中所有的事務源。

但是在Spring的應用中,要想使用分布式事務就有點困難了,好在還有JTOM,它可以做到分布式事務的控制,最終我們可以使用JOTM提供的UserTransaction,而原來的事務控制部分只需要很小的修改。我所經歷的一個項目就是從JBoss遷移到Jetty中去,因為Jetty這樣的Web容器不支持分布式事務,問題也就暴露出來了。所以如果大家在項目中需要遷移一個EJB項目到Spring中,千萬別忽略了分布式事務的控制。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved