spring-tx之事務管理

前言

事務是你們用的最多的,也是比較關心的一個功能。本章就詳細講解spring-tx模塊中事務的實現與spring-boot事務的處理。可能這一節就會把spring-tx說完,事務沒有你們想象的那麼複雜。java

jdbc基本操做

java的鏈接池,事務管理,持久層(MyBtais,hibernation)都是基於jdbc(Java DataBase Connectivity,java數據庫鏈接)進行演進的。那麼仔細看下下面的jdbc代碼,從而理解鏈接池,事務管理,持久層(MyBtais,hibernation)。mysql

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Random;

public class Demo {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");//  1. 註冊驅動
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");  // 2. 建立鏈接
			con.setAutoCommit(false);// 3. 不自動提交,開始事務
		    conn.setReadOnly(true);// 4. 設定只讀,或者其餘鏈接設定
			String sql = "insert into t_user(userName,pwd)values(?,?)"; //  須要執行的sql語句
			ps = conn.prepareStatement( sql );// 5. 在不一樣的statement中設置須要執行的sql語句
			ps.setObject(1, "小高" + i);// 6. 預編譯,參數設置
			ps.setObject(2, "123");
			ps.execute(); // 7. 執行語句
			                        // 8. 若是是查詢插座,對查詢結果進行操做
			con.commit(); // 9. 提交
        } catch (ClassNotFoundException e) {
			 con.rollback(); // 9, 回滾
            e.printStackTrace();
        } catch (SQLException e) {
		    con.rollback(); // 9. 回滾
            e.printStackTrace();
        } finally{
            try {
                if (ps!=null) {
                    ps.close(); // 10. 資源釋放
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (conn!=null) {
                    conn.close(); // 10. 資源釋放
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
鏈接池 事務管理器 持久
1 註冊驅動
2 建立鏈接
3 不自動提交,開始事務
4 設定只讀,或者其餘鏈接設定
5 在不一樣的statement中設置須要執行的sql語句
6 預編譯,參數設置
7 執行語句
8 若是是查詢操做,對查詢結果進行操做
9 提交或者回滾
10 資源釋放 資源釋放

// 時序圖spring

spring事務體系

  1. 事務管理器
  2. 事務Interceptor
  3. spring-boot如何啓動事務

事務管理器

<! ------------------------- 無情分割線 --------------------------------------------------->sql

下面聲明瞭TransactionStatus的接口, 實現了TransactionStatus的抽象類AbstractTransactionStatus,還有AbstractTransactionStatus的子類DefaultTransactionStatus與內部類TransactionInfo,其實只須要TransactionInfo一個類就好了,上面幾個類的細節不講述了。數據庫

爲何spring-tx須要設計那麼多類了。其實就是爲了實現spring中一個很雞肋的功能,事務傳播功能。爲了實現事務傳播功能的設計,代碼還有不少,本人很是不喜歡這個功能,因此不會講述事務傳播的細節,若是有讀者想了解,能夠依據本接內容爲基本自行擴展。 爲何spring-tx須要設計那麼多類了。其實就是爲了實現spring中一個很雞肋的功能,事務傳播功能。爲了實現事務傳播功能的設計,代碼還有不少,本人很是不喜歡這個功能,因此不會講述事務傳播的細節,若是有讀者想了解,能夠依據本接內容爲基本自行擴展。 爲何spring-tx須要設計那麼多類了。其實就是爲了實現spring中一個很雞肋的功能,事務傳播功能。爲了實現事務傳播功能的設計,代碼還有不少,本人很是不喜歡這個功能,因此不會講述事務傳播的細節,若是有讀者想了解,能夠依據本接內容爲基本自行擴展。mybatis

public abstract class AbstractTransactionStatus implements TransactionStatus {
	private boolean rollbackOnly = false;

	private boolean completed = false;

	@Nullable
	private Object savepoint
}
public class DefaultTransactionStatus extends AbstractTransactionStatus {

	@Nullable
	private final Object transaction;

	private final boolean newTransaction;

	private final boolean newSynchronization;

	private final boolean readOnly;

	private final boolean debug;

	@Nullable
	private final Object suspendedResources;
}
protected final class TransactionInfo {

		@Nullable
		private final PlatformTransactionManager transactionManager;

		@Nullable
		private final TransactionAttribute transactionAttribute;

		private final String joinpointIdentification;

		@Nullable
		private TransactionStatus transactionStatus;

		@Nullable
		private TransactionInfo oldTransactionInfo;
}

<! ------------------------- 無情分割線結束 --------------------------------------------------->框架

spring事務的核心體系是PlatformTransactionManager以及實現了DataSourceTransactionManager(數據源事務管理器,好比mybatis,spring-jdbc),JpaTransactionManager(Jpa事務管理器),HibernateTransactionManager(hibernate5事務管理器)。本節直接講解DataSourceTransactionManager,其餘的管理器使用場景很是少不作講解了。dom

public interface PlatformTransactionManager {

	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;

}
  1. getTransaction[ 得到事務 ]
  2. commit[ 提交事務 ]
  3. rollback [ 回滾事務 ]

看下這個方法的調用流程,就很容易懂了。一些其餘的代碼就不要看了,基本是爲了jta作了準備 分佈式

事務Interceptor

TransactionInterceptor 只作了一件事情調用了父類TransactionAspectSupport的invokeWithinTransaction方法,那麼你們把注意力放到TransactionAspectSupport的invokeWithinTransaction方法上吧ide

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {

	// PlatformTransactionManager 事務管理
	public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
		setTransactionManager(ptm);
		setTransactionAttributeSource(tas);
	}


	@Override
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
			@Override
			public Object proceedWithInvocation() throws Throwable {
				return invocation.proceed();
			}
		});
	}
}
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
			throws Throwable {
	    // 獲得對象與方法上的Transactio註解
		final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
		// 獲得事務管理器,@Transactio註解變量transactionManager,能夠指定這個事務由那個事務管理器執行。
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		final String joinpointIdentification = methodIdentification(method, targetClass);

		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			//開啓事務
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			Object retVal = null;
			try {
			    // 執行方法
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// 回滾
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				// 清理TransactionInfo
				cleanupTransactionInfo(txInfo);
			}
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			......
		}
	}
	
	protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {
		// 若是 條件成立,返回默認事務管理器
		if (txAttr == null || this.beanFactory == null) {
			return getTransactionManager();
		}
		// 得到 事務管理器的名字
		String qualifier = txAttr.getQualifier();
		// 若是有
		if (StringUtils.hasText(qualifier)) {
		    // 經過事務管理器的名字得到事務管理器,
			return determineQualifiedTransactionManager(qualifier);
		}
		else if (StringUtils.hasText(this.transactionManagerBeanName)) {
			// 默認管理器名字得到管理器
			return determineQualifiedTransactionManager(this.transactionManagerBeanName);
		}
		else {
		     // 得到默認管理器
			PlatformTransactionManager defaultTransactionManager = getTransactionManager();
			if (defaultTransactionManager == null) {
				defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
				this.transactionManagerCache.putIfAbsent(
						DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
			}
			return defaultTransactionManager;
		}
	}
	// 經過事務管理器的名字得到事務管理器
	private PlatformTransactionManager determineQualifiedTransactionManager(String qualifier) {
		PlatformTransactionManager txManager = this.transactionManagerCache.get(qualifier);
		if (txManager == null) {
			txManager = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
					this.beanFactory, PlatformTransactionManager.class, qualifier);
			this.transactionManagerCache.putIfAbsent(qualifier, txManager);
		}
		return txManager;
	}

spring-boot如何啓動事務

spring-boot啓動事務很簡單隻須要EnableTransactionManagement註解或者TransactionManagementConfigurationSelector被ApplicationContext加載,那麼就會自動啓動事務。詳細請看下面的流程

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
		// 建立Advisor
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		// 建立 註解識別
		advisor.setTransactionAttributeSource(transactionAttributeSource());
		// 建立方法攔截器與處理器
		advisor.setAdvice(transactionInterceptor());
		advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {
		return new AnnotationTransactionAttributeSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor() {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource());
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}
}

總結

事務真的很簡單,可是爲了作不少的兼容於擴展,spring-tx寫得很複雜。我的感受在分佈式的微服務框架設計下,不少都不該該要了。

相關文章
相關標籤/搜索