1. 程式人生 > >Java中的事務及使用

Java中的事務及使用

什麼是事務?

事務(Transaction),一般是指要做的或所做的事情。在計算機術語中是指訪問並可能更新資料庫中各種資料項的一個程式執行單元(unit)。事務通常由高階資料庫操縱語言或程式語言(如SQL,C++或Java)書寫的使用者程式的執行所引起,並用形如begin transactionend transaction語句(或函式呼叫)來界定。事務由事務開始(begin transaction)和事務結束(end transaction)之間執行的全體操作組成。

為什麼要事務?

事務是為解決資料安全操作提出的,事務控制實際上就是控制資料的安全訪問。

用一個簡單例子說明:銀行轉帳業務,賬戶A要將自己賬戶上的1000元轉到B賬戶下面,A賬戶餘額首先要減去1000元,然後B賬戶要增加1000元。假如在中間網路出現了問題,A賬戶減去1000元已經結束,B因為網路中斷而操作失敗,那麼整個業務失敗,必須做出控制,要求A賬戶轉帳業務撤銷。這才能保證業務的正確性,完成這個操走就需要事務,將A賬戶資金減少和B賬戶資金增加

放到同一個事務裡,要麼全部執行成功,要麼全部撤銷,這樣就保證了資料的安全性。

事務的4個特性(ACID):

1) 原子性(atomicity):事務是資料庫的邏輯工作單位,而且是必須是原子工作單位,對於其資料修改,要麼全部執行,要麼全部不執行。

2) 一致性(consistency):事務在完成時,必須是所有的資料都保持一致狀態。在相關資料庫中,所有規則都必須應用於事務的修改,以保持所有資料的完整性。(例項:轉賬,兩個賬戶餘額相加,值不變。)

3) 隔離性(isolation):一個事務的執行不能被其他事務所影響。

4) 永續性(durability):一個事務一旦提交,事物的操作便永久性的儲存在DB中。

即便是在資料庫系統遇到故障的情況下也不會丟失提交事務的操作。

Java有幾種型別的事務?

Java事務的型別有三種:JDBC事務、JTA(Java Transaction API)事務、容器事務。

1.JDBC事務

在JDBC中處理事務,都是通過Connection完成的。同一事務中所有的操作,都在使用同一個Connection物件。JDBC事務預設是開啟的,並且是預設提交

JDBC Connection 介面提供了兩種事務模式:自動提交和手工提交

JDBC中的事務java.sql.Connection 的三個方法與事務有關:

setAutoCommit(boolean):設定是否為自動提交事務,如果true(預設值為true)表示自動提交,也就是每條執行的SQL語句都是一個單獨的事務,如果設定為false,

需要手動提交事務。

commit():提交結束事務。

rollback():回滾結束事務。

傳統JDBC操作流程:

  1).獲取JDBC連線   2).宣告SQL   3).預編譯SQL   4).執行SQL   5).處理結果集   

  6).釋放結果集  7).釋放Statement  8).提交事務  9).處理異常並回滾事務 10).釋放JDBC連線

JDBC優缺點:1.冗長、重複     2.顯示事務控制     3.每個步驟不可獲取    4.顯示處理受檢查異常

JDBC為使用Java進行資料庫的事務操作提供了最基本的支援。通過JDBC事務,我們可以將多個SQL語句放到同一個事務中,保證其ACID特性。JDBC事務的主要優點就是API比較簡單,可以實現最基本的事務操作,效能也相對較好。但是,JDBC事務有一個侷限:一個 JDBC 事務不能跨越多個數據庫!所以,如果涉及到多資料庫的操作或者分散式場景,JDBC事務就無能為力了。

 2.JTA事務

JTA(Java Transaction API)提供了跨資料庫連線(或其他JTA資源)的事務管理能力。JTA事務管理則由JTA容器實現,J2ee框架中事務管理器與應用程式,資源管理器,以及應用伺服器之間的事務通訊。

1)JTA的構成

a、高層應用事務界定介面,供事務客戶界定事務邊界的

b、X/Open XA協議(資源之間的一種標準化的介面)的標準Java對映,它可以使事務性的資源管理器參與由外部事務管理器控制的事務中

c、高層事務管理器介面,允許應用程式伺服器為其管理的應用程式界定事務的邊界

2)JTA的主要介面位於javax.transaction包中

a、UserTransaction介面:讓應用程式得以控制事務的開始、掛起、提交、回滾等。由Java客戶端程式或EJB呼叫。

b、TransactionManager 介面:用於應用伺服器管理事務狀態

c、Transaction介面:用於執行相關事務操作

d、XAResource介面:用於在分散式事務環境下,協調事務管理器和資源管理器的工作

e、Xid介面:為事務識別符號的Java對映

:前3個介面位於Java EE版的類庫 javaee.jar 中,Java SE中沒有提供!UserTransaction是程式設計常用的介面,JTA只提供了介面,沒有具體的實現。

JTS(Java Transaction Service)是服務OTS的JTA的實現。簡單的說JTS實現了JTA介面,並且符合OTS的規範。

JTA的事務週期可橫跨多個JDBC Connection生命週期,對眾多Connection進行排程,實現其事務性要求。

JTA可以處理任何提供符合XA介面的資源。包括:JDBC連線,資料庫,JMS,商業物件等等。

3)JTA程式設計的基本步驟

a、首先配置JTA ,建立相應的資料來源

b、建立事務:通過建立UserTransaction類的例項來開始一個事務。程式碼如下:

    Context ctx = new InitialContext(p) ;

    UserTransaction trans = (UserTransaction) ctx.lookup("javax. Transaction.UserTransaction")

c、開始事務:程式碼為 trans.begin() ;

d、找出資料來源:從Weblogic Server上找到資料來源,程式碼如下:

DataSource ds = (DataSource) ctx.lookup(“mysqldb") ;

e、建立資料庫連線:Connection mycon = ds.getConnection() ;

f、執行SQL操作:stmt.executeUpdate(sqlS);

g、完成事務:trans.commit(); / trans.rollback();

h、關閉連線:mycon.close() ;

JTA的優缺點:

JTA的優點很明顯,就是提供了分散式事務的解決方案,嚴格的ACID。但是,標準的JTA方式的事務管理在日常開發中並不常用。

JTA的缺點是實現複雜,通常情況下,JTA UserTransaction需要從JNDI獲取。這意味著,如果我們使用JTA,就需要同時使用JTA和JNDI。

JTA本身就是個笨重的API,通常JTA只能在應用伺服器環境下使用,因此使用JTA會限制程式碼的複用性。

3、Spring容器事務

Spring事務管理的實現有許多細節,如果對整個介面框架有個大體瞭解會非常有利於我們理解事務,下面通過講解Spring的事務介面來了解Spring實現事務的具體策略。

Spring事務管理涉及的介面及其聯絡:

Spring並不直接管理事務,而是提供了多種事務管理器,他們將事務管理的職責委託給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。 Spring事務管理器的介面是org.springframework.transaction.PlatformTransactionManager,通過這個介面,Spring為各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,但是具體的實現就是各個平臺自己的事情了。

Public interface PlatformTransactionManager{  
       // 由TransactionDefinition得到TransactionStatus物件
       TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    // 提交
       Void commit(TransactionStatus status) throws TransactionException;  
       // 回滾
       Void rollback(TransactionStatus status) throws TransactionException;  
}

1)、Spring JDBC事務
如果應用程式中直接使用JDBC來進行持久化,DataSourceTransactionManager會為你處理事務邊界。為了使用     DataSourceTransactionManager,你需要使用如下的XML將其裝配到應用程式的上下文定義中:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="dataSource" />
</bean>

  實際上,DataSourceTransactionManager是通過呼叫java.sql.Connection來管理事務。通過呼叫連線的commit()方法來提交事務,同樣,事務失敗則通過呼叫rollback()方法進行回滾。

2)、Hibernate事務

如果應用程式的持久化是通過Hibernate實現的,那麼你需要使用HibernateTransactionManager。對於Hibernate3,需要在Spring上下文定義中新增如下的<bean>宣告:

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
     <property name="sessionFactory" ref="sessionFactory" />
</bean>

sessionFactory屬性需要裝配一個Hibernate的session工廠,HibernateTransactionManager的實現細節是它將事務管理的職責委託給org.hibernate.Transaction物件,而後者是從Hibernate Session中獲取到的。當事務成功完成時,HibernateTransactionManager將會呼叫Transaction物件的commit()方法,反之,將會呼叫rollback()方法。

3)、Java持久化API事務(JPA)

Hibernate多年來一直是事實上的Java持久化標準,但是現在Java持久化API作為真正的Java持久化標準進入大家的視野。如果你計劃使用JPA的話,那你需要使用Spring的JpaTransactionManager來處理事務。你需要在Spring中這樣配置JpaTransactionManager:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
     <property name="sessionFactory" ref="sessionFactory" />
</bean>

JpaTransactionManager只需要裝配一個JPA實體管理工廠(javax.persistence.EntityManagerFactory介面的任意實現)。JpaTransactionManager將與由工廠所產生的JPA EntityManager合作來構建事務。

基本的事務屬性的定義:

事務管理器介面PlatformTransactionManager通過getTransaction(TransactionDefinition definition)方法來得到事務,這個方法裡面的引數是TransactionDefinition類,這個類就定義了一些基本的事務屬性。

事務屬性可以理解成事務的一些基本配置,描述了事務策略如何應用到方法上。

事務屬性包含了5個方面:

傳播行為、隔離規則、回滾規則、事務超時、是否只讀

TransactionDefinition:

public interface TransactionDefinition {
    int getPropagationBehavior(); // 返回事務的傳播行為
    int getIsolationLevel(); // 返回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些資料
    int getTimeout();  // 返回事務必須在多少秒內完成
    boolean isReadOnly(); // 事務是否只讀,事務管理器能夠根據這個返回值進行優化,確保事務是隻讀的
}

7種傳播行為:

PROPAGATION_REQUIRED:如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。

PROPAGATION_SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行。

PROPAGATION_MANDATORY:支援當前事務,如果當前沒有事務,就丟擲異常。

PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。

PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則丟擲異常。 

雖然有7種,但是常用的就第一種REQUIRED和第四種REQUIRES_NEW

五個隔離級別:

ISOLATION_DEFAULT:這是一個PlatfromTransactionManager預設的隔離級別,使用資料庫預設的事務隔離級別.

另外四個與JDBC的隔離級別相對應;

ISOLATION_READ_UNCOMMITTED:這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的資料。

這種隔離級別會產生髒讀,不可重複讀和幻像讀。

ISOLATION_READ_COMMITTED:保證一個事務修改的資料提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的資料。

這種事務隔離級別可以避免髒讀出現,但是可能會出現不可重複讀和幻像讀。

ISOLATION_REPEATABLE_READ:這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。

它除了保證一個事務不能讀取另一個事務未提交的資料外,還保證了避免下面的情況產生(不可重複讀)。

ISOLATION_SERIALIZABLE:這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。

除了防止髒讀,不可重複讀外,還避免了幻像讀。

事務的屬性可同通過註解方式或配置檔案配置:

註解方式:

@Transactional只能被應用到public方法上,對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能.
預設情況下,一個有事務方法, 遇到RuntimeException 時會回滾 . 遇到 受檢查的異常 是不會回滾 的. 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) 

@Transactional(
    readOnly = false, //讀寫事務
    timeout = -1 ,     //事務的超時時間,-1為無限制
    noRollbackFor = ArithmeticException.class, //遇到指定的異常不回滾
    isolation = Isolation.DEFAULT, //事務的隔離級別,此處使用後端資料庫的預設隔離級別
    propagation = Propagation.REQUIRED //事務的傳播行為
)

配置檔案( aop攔截器方式):

<tx:advice id="advice" transaction-manager="txManager">
          <tx:attributes>
            <!-- tx:method的屬性:
                * name 是必須的,表示與事務屬性關聯的方法名(業務方法名),對切入點進行細化。萬用字元 
                     (*)可以用來指定一批關聯到相同的事務屬性的方法。
                      如:'get*'、'handle*'、'on*Event'等等.
                * propagation:不是必須的,預設值是REQUIRED表示事務傳播行為,
                  包括REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTED
                * isolation:不是必須的 預設值DEFAULT ,表示事務隔離級別(資料庫的隔離級別)
                * timeout:不是必須的 預設值-1(永不超時),表示事務超時的時間(以秒為單位)
                * read-only:不是必須的 預設值false不是隻讀的表示事務是否只讀?
                * rollback-for: 不是必須的表示將被觸發進行回滾的 Exception(s);以逗號分開。
                   如:'com.foo.MyBusinessException,ServletException'
                * no-rollback-for:不是必須的表示不被觸發進行回滾的 Exception(s),以逗號分開。                                        
                   如:'com.foo.MyBusinessException,ServletException'   
                任何 RuntimeException 將觸發事務回滾,但是任何 checked Exception 將不觸發事務回滾                     
            -->
             <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
             <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
             <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"  rollback-for="Exception"/>
             <!-- 其他的方法之只讀的 -->
             <tx:method name="*" read-only="true"/>
          </tx:attributes>
</tx:advice>