Spring筆記 - 事務管理

1. 事務管理

1.1 概述

事務的屬性,ACID
java

  • 原子性Atomic,事務由一個或多個活動組成,這些活動只能所有成功或所有失敗,任何一個活動失敗,都將致使事務回滾mysql

  • 一致性Consistent,事務的完成,不論成功或失敗,業務必須處於一致的狀態程序員

  • 隔離性Isolation,多個並行的事務之間保持隔離spring

  • 持久性Durable,事務完成後,事務的結果須要持久化sql


1.2 JDBC事務管理

JDBC基於鏈接進行事務管理,默認開啓自動提交。缺點在於不能跨數據庫;且在使用鏈接池時,難以保證線程安全,可能出現死鎖等問題;若是不使用鏈接池,又容易出現效率問題。
數據庫

try {
    conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/demoDb", username, userpassword);
    if(conn.getAutoCommit())
        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 ex) {
        ex.printStackTrace();
    }
}


1.3 JTA事務管理

[參考]
express

- Java Transaction API,Java事務API,支持分佈式事務服務。即在兩個或多個網絡計算機資源上訪問而且更新數據,這些數據能夠分佈在多個數據庫上。JDBC驅動程序的JTA支持極大地加強了數據訪問能力。 apache

- 若是使用 JTA 界定事務,就須要有一個實現 javax.sql.XADataSource 、javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驅動程序。一個實現了這些接口的驅動程序將能夠參與 JTA 事務。一個 XADataSource 對象就是一個 XAConnection 對象的工廠。XAConnections 是參與 JTA 事務的 JDBC 鏈接。須要用應用服務器的管理工具設置 XADataSource 。從應用服務器和 JDBC 驅動程序的文檔中能夠了解到相關的指導。J2EE 應用程序用 JNDI 查詢數據源。一旦應用程序找到了數據源對象,它就調用 javax.sql.DataSource.getConnection() 以得到到數據庫的鏈接。 編程

- XA 鏈接與非 XA 鏈接不一樣。XA 鏈接參與了 JTA 事務。這意味着 XA 鏈接不支持 JDBC 的自動提交功能。應用程序應該使用 UserTransaction.begin()、 UserTransaction.commit() 和 serTransaction.rollback() ,而不能對 XA 鏈接調用 java.sql.Connection.commit() 或者 java.sql.Connection.rollback() 。安全

- JTA事務管理缺點在於實現複雜,侵入性大


1.4 Java EE容器事務

-  容器事務主要是Java EE應用服務器提供的,大多基於JTA,須要JNDI的支持,實現複雜。

- 相對編碼實現JTA 事務管理,能夠經過EJB容器提供的容器事務管理機制(CMT)完成同一個功能,這項功能由Java EE應用服務器提供,能夠簡單的指定將哪一個方法加入事務,一旦指定,容器將負責事務管理任務。經過這種方式能夠將事務代碼排除在邏輯編碼以外,同時將全部困難交給 Java EE容器去解決。

- 使用EJB CMT的另一個好處就是程序員無需關心JTA API的編碼,不過,理論上必須使用EJB。


1.5 Spring事務管理

1.5.1 優勢

- 爲不一樣的事務API提供統一的編程模型,事務API包括JTA、JDBC、Hibernate、JPA、JDO

- 支持聲明式事務管理

- 提供比JTA更簡單的編程式事務管理API

- 自然集成Spring數據訪問抽象

1.5.2 Spring事務管理基礎

- Spring並不直接管理事務,而是把事務管理的職責委託給JTA或其餘持久化機制提供的平臺相關的事務實現。

- PlatformTransactionManager是Spring事務的基礎,可經過其實現管理事務,其實現例如:DataSourceTransactionManager、HibernateTransactionManager、JpaTransactionManager、JtaTransactionManager、JmsTransactionManager

public interface PlatformTransactionManager {
    TransactionStatus /* 線程狀態設置和讀取 */
        getTransaction(TransactionDefinition definition /* 定義txn的隔離、傳播、超時、只讀參數 */) throws TransactionException; // 非檢查型的Exception
    void commit(TransactionStatus status) throws TransactionException; // 提交
    void rollback(TransactionStatus status) throws TransactionException; // 回滾
}

public interface TransactionStatus extends SavepointManager {
    boolean isNewTransaction();
    boolean hasSavepoint();
    void setRollbackOnly();
    boolean isRollbackOnly();
    void flush();
    boolean isCompleted();
}
<!-- 配置各類TxnManager -->
<!-- JDBC數據源 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!-- Hibernate數據源 -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- JTA數據源 -->
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
// 使用TxnManager
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
    // 業務邏輯代碼 ...
}
catch (MyException ex) {
    txManager.rollback(status);
    throw ex;
}
txManager.commit(status);

1.5.3 使用事務同步資源

1.5.3.1 低層次方法

- 使用DataSourceUtils  (JDBC),  EntityManagerFactoryUtils  (JPA), SessionFactoryUtils (Hibernate), PersistenceManagerFactoryUtils (JDO)

// 當前線程中只會取得一個鏈接
// 而dataSource.getConnection()不一樣,每次可能取得不一樣的鏈接,視數據源例如鏈接池而定
Connection conn = DataSourceUtils.getConnection(dataSource);
DataSourceUtils.releaseConnection(conn, dataSource);

- 使用TransactionAwareDataSourceProxy,它是DataSource的代理,相似於JNDI dataSource。是很是底層的實現,不建議使用

1.5.3.2 高層次方法

- 使用基於Spring數據訪問模板例如JdbcTemplate的持久化集成API

- 使用事務感知工廠Bean或代理來調用ORM自身的API

1.5.4 編程式事務管理

- 使用TransactionTemplate,更爲推薦

public class UserService implements Service {
    private TransactionTemplate txnTempl;
    public UserService(PlatformTransactionManager txnMgr) {
        this.txnTempl = new TransactionTemplate(txnMgr);
    }
    
    // 有返回值的操做
    public Object someAction1() {
        return txnTempl.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus status) {
                updateOperation1();
                return resultOfUpdateOperation2();
            }
        };
    }

    // 無返回值的操做
    public Object someAction2() {
        return txnTempl.execute(new TransactionCallbackWithoutResult() {
            public Object doInTransaction(TransactionStatus status) {
                updateOperation1();
                updateOperation2();
            }
        };
    }
}

- 使用PlatformTransactionManager,例子見1.5.2

1.5.5 聲明式事務管理

- Spring聲明式事務管理相似於EJB CMT,但既能夠支持JTA事務,也能夠支持JDBC、JPA、Hibernate等;能夠應用於非EJB的類;提供聲明式的回滾設置;還能夠爲指定異常類型配置回滾機制;不支持跨遠程調用(用戶線程)的事務管理

- Spring 2.0以後無需使用TransactionProxyFactoryBean

- 經過AOP Proxy實現

1.5.5.1 設置方法

<!-- XML設置方法 -->
<!-- 業務方法 -->
<bean id="stockService" class="x.y.z.service.DefaultStockService"/>
<!-- 數據源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<!-- 事務管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!-- 事務通知,這是xml設置方式,還支持tx註解,啓用方法<tx:annotation-driven/> -->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- read-only,事務只讀;rollback-for,默認爲非檢查型例如RuntimeException回滾,檢查型不回滾 -->
        <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/> 
        <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
        <!-- propagation,傳播性,默認爲Required;timeout默認爲-1即永不超時 -->
        <tx:method name="*" propagation="REQUIRED" timeout="30" />
    </tx:attributes>
</tx:advice>
<!-- 事務切面 -->
<aop:config>
    <aop:pointcut id="stockServiceOperation" expression="execution(* x.y.service.StockService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="stockServiceOperation"/>
</aop:config>
// 啓用註解
@EnableTransactionManagement
@Configuration
public AppConfig {
    // ...
}

// 註解設置方法
// 在public方法上設置,AspectJ支持非public方法
@Transactional(propagation = Propagation.REQUIRED, timeout=30, isolation=Isolation.DEFAULT) 
public void updateStock() {
    // ...
}
// 推薦爲具體類而非接口設置註解
@Transactional(readOnly = true)
public class QueryStockService implements Service {

}

1.5.5.2 多事務管理器支持

各方法運行在各自的事務管理器中

public class TransactionalService {
    @Transactional("order")
    public void setSomething(String name) { ... }
    @Transactional("account")
    public void doSomething() { ... }
}
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    <qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    <qualifier value="account"/>
</bean>

1.5.5.3 自定義註解快捷方式

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("order")
public @interface OrderTx {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("account")
public @interface AccountTx {
}

@OrderTx
public void setSomething(String name) { ... }
@AccountTx
public void doSomething() { ... }

1.5.6 事務傳播性Propagation

類型 說明
PROPAGATION_REQUIRED 若是存在一個事務,則在當前事務中運行。無則開啓;Spring默認
PROPAGATION_SUPPORTS 若是存在一個事務,則在當前事務中運行。無則無事務運行
PROPAGATION_MANDATORY 若是存在一個事務,則在當前事務中運行。無則拋出異常
PROPAGATION_REQUIRES_NEW 若是存在一個事務,則掛起,並開啓一個新事務運行
PROPAGATION_NOT_SUPPORTED 若是存在一個事務,則掛起,並沒有事務運行
PROPAGATION_NEVER 若是存在一個事務,則拋出異常
PROPAGATION_NESTED 若是存在一個事務,則開啓一個嵌套的事務運行,嵌套事務不影響外部的事務;無則開啓事務運行

1.5.7 隔離級別Isolation Level

- Spring框架提供程序級別的隔離特性,有別於數據庫自身提供的隔離特性

 

Dirty Reads

髒讀,讀到了其餘事務未提交的更新數據

Non-Repeatable Reads

不可重複讀,在一次事務中,先後讀取的數據不一致,一般是後面一次讀取到了其餘事務的更新數據

Phantom Reads

幻讀,在一次事務中,先後讀取的數據不一致,一般是後面一次讀取到了其餘事務的新增數據

Serializable N N N
Repeatable Read N N Y
Read Committed (Spring默認) N Y Y
Read Uncommitted Y Y Y

大多數數據庫的默認隔離級別爲Read Commited,如Sql Server、Oracle

少數數據庫默認的隔離級別爲Repeatable Read, 如MySQL InnoDB存儲引擎

相關文章
相關標籤/搜索