我在面試中經常會問到這樣的一個問題,假如有一個全局變量,在一個事務中修改了這個變量的值,而后這個事務因為別的原因回滾了,那這個變量的值會回滾到更改之前的值么?
其實事務只能對它所管理的資源進行提交和回滾,這些資源就是事務源,它通常包括數據庫連接資源,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時事務會回滾。如果大家也在使用這種事務控制方式的話,還是主動告訴容器何時回滾,何時提交事務吧,采用默認值并不是一個好的辦法
新聞熱點
疑難解答
圖片精選