Spring 事務管理

(一)電商系統的服務端開發中,常常會遇到須要同時更改多個數據源的場景,好比訂單支付系統,用戶轉帳等等。在服務器出現異常的狀況下,也須要保證數據的一致性,於是須要把對於多個數據源的修改封裝在同一個事務中進行。Spring中的事物管理包括編程式和聲明式兩種。java

 

(二)Spring編程式事務管理spring

  • 事務定義:TransactionDefinition
  • 事務狀態:TransactionStatus
  • 事務管理者:PlatformTransactionManager

1)TransactionDefinition數據庫

public interface TransactionDefinition {

	
	int PROPAGATION_REQUIRED = 0;
	int PROPAGATION_SUPPORTS = 1;
	int PROPAGATION_MANDATORY = 2;
	int PROPAGATION_REQUIRES_NEW = 3;
	int PROPAGATION_NOT_SUPPORTED = 4;
	int PROPAGATION_NEVER = 5;
	int PROPAGATION_NESTED = 6;

	int ISOLATION_DEFAULT = -1;
	int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
	int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
	int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
	int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

	int TIMEOUT_DEFAULT = -1;


	
	int getPropagationBehavior();
	int getIsolationLevel();
	int getTimeout();
	boolean isReadOnly();
	String getName();

}

接口TransactionDefinition定義了事務的7種傳播行爲和4種隔離級別apache

  • TransactionDefinition.PROPAGATION_REQUIRED:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。這是默認值。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,若是當前存在事務,則拋出異常。
  • TransactionDefinition.PROPAGATION_MANDATORY:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
  • TransactionDefinition.PROPAGATION_NESTED:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
  • 四種隔離級別爲讀未提交、讀提交、重複讀、串行化。
  • getTimeout()返回超時時間,事物若不能按時完成,則自動回滾,默認設置爲底層事物系統的超時值,如底層數據庫的超時值。
  • isReadOnly()返回該事物是否爲只讀的事物。

    「只讀事務」並非一個強制選項,它只是一個「暗示」,提示數據庫驅動程序和數據庫系統,這個事務並不包含更改數據的操做,那麼JDBC驅動程序和數據庫就有可能根據這種狀況對該事務進行一些特定的優化,比方說不安排相應的數據庫鎖,以減輕事務對數據庫的壓力,畢竟事務也是要消耗數據庫的資源的。 編程

    可是你非要在「只讀事務」裏面修改數據,也並不是不能夠,只不過對於數據一致性的保護不像「讀寫事務」那樣保險而已。 數組

    所以,「只讀事務」僅僅是一個性能優化的推薦配置而已,並不是強制你要這樣作不可性能優化

  • 回滾規則:能夠明確配置拋出哪些異常時進行回滾,拋出哪些異常時不回滾,默認配置下,unchecked異常(運行期異常RunTimeException.class,編譯器不會檢查到的異常)會回滾,checked(非運行期異常Exception.class,能夠被編譯器檢查到,須要捕獲)異常不會回滾。還能夠經過調用setRollbackOnly()方法來指示一個事物必須回滾。

 

2)TransactionStatus服務器

package org.springframework.transaction;

import java.io.Flushable;


public interface TransactionStatus extends SavepointManager, Flushable {

	
	boolean isNewTransaction();

	boolean hasSavepoint();

	void setRollbackOnly();

	boolean isRollbackOnly();

	@Override
	void flush();

	boolean isCompleted();

}
  • setRollbackOnly()設置該事務爲只能回滾,當對該事務執行commit時,實際執行的是回滾操做。
  • isRollbackOnly()返回該事務是否爲只能回滾。

 

 

3)PlatformTransactionManager併發

package org.springframework.transaction;


public interface PlatformTransactionManager {

	
	TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

	
	void commit(TransactionStatus status) throws TransactionException;

	
	void rollback(TransactionStatus status) throws TransactionException;

}

       getTransaction根據事務的定義返回一個已有的或是新建一個事務。我估計是根據事務的名稱返回一個已有的事務,不然則根據事務定義的傳播行爲、隔離級別、超時時間新建一個事務。getTransaction的實現若遇到了一個本身不支持的事務定義,則須要拋出異常。app

        commit提交事務。若事務被標記爲回滾,則執行回滾;若事務是新事務,那麼提交以後會從新執行先前因生成該事務而暫停的其餘事務;事務提交前產生的異常會致使事務提交失敗。

         rollback回滾事務。若先前此事務提交失敗則不能夠執行回滾操做。


 

    直接使用PlatformTransactionManager接口的實現去進行事務管理比較麻煩,Spring提供了TransactionTemplate事務模版,該模板封裝了事物管理者生成事物、提交事物、回滾事物的操做,方便咱們在業務代碼中適用事務。因此若要使用編程式的事物管理,通常推薦使用TransactionTemplate而不是直接使用PlatformTransactionManager。

4)TransactionTemplate

package org.springframework.transaction.support;

import java.lang.reflect.UndeclaredThrowableException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.TransactionSystemException;


@SuppressWarnings("serial")
public class TransactionTemplate extends DefaultTransactionDefinition
		implements TransactionOperations, InitializingBean {

	/** Logger available to subclasses */
	protected final Log logger = LogFactory.getLog(getClass());

	private PlatformTransactionManager transactionManager;

	public TransactionTemplate() {
	}

	public TransactionTemplate(PlatformTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
		super(transactionDefinition);
		this.transactionManager = transactionManager;
	}

	public void setTransactionManager(PlatformTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	public PlatformTransactionManager getTransactionManager() {
		return this.transactionManager;
	}

	@Override
	public void afterPropertiesSet() {
		if (this.transactionManager == null) {
			throw new IllegalArgumentException("Property 'transactionManager' is required");
		}
	}


	@Override
	public <T> T execute(TransactionCallback<T> action) throws TransactionException {
		if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
			return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
		}
		else {
			TransactionStatus status = this.transactionManager.getTransaction(this);
			T result;
			try {
				result = action.doInTransaction(status);
			}
			catch (RuntimeException ex) {
				// Transactional code threw application exception -> rollback
				rollbackOnException(status, ex);
				throw ex;
			}
			catch (Error err) {
				// Transactional code threw error -> rollback
				rollbackOnException(status, err);
				throw err;
			}
			catch (Throwable ex) {
				// Transactional code threw unexpected exception -> rollback
				rollbackOnException(status, ex);
				throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
			}
			this.transactionManager.commit(status);
			return result;
		}
	}

	private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
		logger.debug("Initiating transaction rollback on application exception", ex);
		try {
			this.transactionManager.rollback(status);
		}
		catch (TransactionSystemException ex2) {
			logger.error("Application exception overridden by rollback exception", ex);
			ex2.initApplicationException(ex);
			throw ex2;
		}
		catch (RuntimeException ex2) {
			logger.error("Application exception overridden by rollback exception", ex);
			throw ex2;
		}
		catch (Error err) {
			logger.error("Application exception overridden by rollback error", ex);
			throw err;
		}
	}

}

      TransactionTemplate內部包含了事物管理者PlatformTransactionManager transactionManager,而且繼承了DefaultTransactionDefinition,即擁有了事物定義的信息。

       <T> T execute(TransactionCallback<T> action)是TransactionTemplate內部的一個公有方法,事物的提交回滾,以及具體的業務邏輯都在該方法中實現,該方法的參數TransactionCallback<T> action是一個接口,須要開發人員自行實現該接口的一個方法doInTransaction(TransactionStatus status)。

     在execute方法中,首先經過transactionManager.getTransaction(this)開啓一個事物,獲取事物狀態status;而後具體的業務邏輯在doInTransaction(TransactionStatus status)中編寫;最後,若無任何異常,則執行事物提交的操做transactionManager.commit(TransactionStatus status),不然,捕獲異常,執行事物的回滾操做transactionManager.rollback(TransactionStatus status);在業務邏輯中,可根據具體業務須要,設置status.setRollbackOnly(),指定事物回滾。

        若不須要返回值,則可使用TransactionCallbackWithoutResult代替TransactionCallback,並調用方法doInTransactionWithoutResult代替doInTransaction。

 

 

(三)Spring聲明式事務管理

       聲明式事物管理是基於AOP的,即對調用方法進行攔截,在執行目標方法前開啓一個事物,在執行目標方法後根據狀況提交事物或是回滾事物。

      聲明式的事物管理器有兩種,一種是經過在類、接口或方法上添加@Transactional註解,一種是經過xml配置文件。

1)@Transactional註解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	@AliasFor("transactionManager")
	String value() default "";

	@AliasFor("value")
	String transactionManager() default "";

	Propagation propagation() default Propagation.REQUIRED;

	Isolation isolation() default Isolation.DEFAULT;

	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	boolean readOnly() default false;

	String[] rollbackForClassName() default {};

	Class<? extends Throwable>[] noRollbackFor() default {};

	String[] noRollbackForClassName() default {};

}
value 事物管理器
propagation    

設置事務的傳播行爲。

@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

isolation 設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務併發的狀況
timeout 設置事物的超時時間,-1表明永遠不超時
readOnly

設置當前事務是否爲只讀事務,設置爲true表示只讀,false則表示可讀寫,默認值爲false。@Transactional(readOnly=true)

rollbackFo

設置須要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。

@Transactional(rollbackFor={RuntimeException.class, Exception.class})

rollbackForClassName

設置須要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。

@Transactional(rollbackForClassName={"RuntimeException","Exception"})

noRollbackFor

設置不須要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。

@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

設置不須要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。

@Transactional(noRollbackForClassName={"RuntimeException","Exception"})

2)使用xml

 

3)注意

  1. @Transactional註解只對public方法有效;
  2. 只有來自外部的方法調用纔會被捕獲,並引發事物行爲;
  3. 不建議在接口或接口方法上使用該註解,由於這隻有在使用基於接口的代理時纔會生效。

 

 

(三)兩種事物管理方式的比較

       使用編程式事物管理可使得事物的粒度細化到代碼塊級別,可是在不可避免地會致使業務代碼的實現和事物管理的代碼耦合。

       使用聲明式事物管理能夠避免侵入式編程,不須要在業務代碼中摻瑣事務管理的代碼,只須要在配置文件中作相關的事物規則聲明,使得業務代碼不受污染,可是其封裝粒度只能到達方法級別。

 

(四)自動提交

       默認狀況下,數據庫處於自動提交模式。每一條語句就是一個單獨的事物,該語句執行完畢後,若成功則隱式提交事物,若失敗則隱式回滾事物。可是對於正常的事物管理,是一組相關的操做處於一個事物之中,於是須要關閉數據庫的自動提交。spring會將底層鏈接的自動提交設置爲false。

相關文章
相關標籤/搜索