Spring 聲明事務中transactionAttributes屬性 + - Exception 實現邏輯

下面是一段典型的Spring 聲明事務的配置:

	<bean id="userDAOProxy"
		class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="transactionManager">
			<ref bean="transactionManager" />
		</property>
		<property name="target">
			<ref local="userDAO" />
		</property>
		<property name="transactionAttributes">
			<props>
				<prop key="insert*">PROPAGATION_REQUIRED</prop>
				<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
				<prop key="save*">PROPAGATION_REQUIRED,-ApplicationException,+BusinessException</prop>
			</props>
		</property>
	</bean>

在Spring聲明事務中,我們可以自定義方法的哪些Exception需要回滾,哪些Exception可以直接提交。

通過下面的配置:

<prop key="save*">PROPAGATION_REQUIRED,-ApplicationException,+BusinessException</prop>

- 表示拋出該異常時需要回滾

+表示即使拋出該異常事務同樣要提交

-ApplicationException :表示拋出ApplicationException 時,事務需要回滾。但不是說只拋出ApplicationException 異常時,事務纔回滾,如果程序拋出RuntimeException和Error時,事務一樣會回滾,即使這裏沒有配置。因爲Spring中默認對所有的RuntimeException和Error都會回滾事務

Spring中是如何實現這段邏輯的:

調用的是

org.springframework.transaction.interceptor.RuleBasedTransactionAttribute.rollbackOn(Throwable ex)

public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}

		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;

		//配置文件中的回滾異常列表,當然去掉了-,只有name,commit的規則是另外一個對象
		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				//使用拋出exception的className(全路徑className)進行indexOf match
				//如果沒有match上會繼續搜索superClass name進行match,到Throwable class爲止
				int depth = rule.getDepth(ex);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}

		// User superclass behavior (rollback on unchecked) if no rule matches.
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			//如果沒有match上,調用此方法繼續match,判斷instance of RuntimeException or Error
			return super.rollbackOn(ex);
		}

		return !(winner instanceof NoRollbackRuleAttribute);
	}

rule.getDepth方法代碼

        因爲使用的是className的全路徑進行indexOf匹配,所以如果自定義異常是:com.abc.ApplicationException,你在xml配置文件中定義爲:-abc,同樣會match上,事務也會回滾,這一點需要注意。

       另外一點,如何在xml中定義的是-Exception,這樣只要class的全路徑中包含Exception字段,如包名,也會匹配上。

        public int getDepth(Throwable ex) {
		return getDepth(ex.getClass(), 0);
	}


	private int getDepth(Class exceptionClass, int depth) {
		if (exceptionClass.getName().indexOf(this.exceptionName) != -1) {
			// Found it!
			return depth;
		}
		// If we've gone as far as we can go and haven't found it...
		if (exceptionClass.equals(Throwable.class)) {
			return -1;
		}
		return getDepth(exceptionClass.getSuperclass(), depth + 1);
	}

super.rollbackOn(Throwable ex) 方法代碼

很簡單的一行代碼,這就是爲什麼RuntimeException和Error也會回滾啦。

	public boolean rollbackOn(Throwable ex) {
 		return (ex instanceof RuntimeException || ex instanceof Error);
 	}


幾次測試輸出的debug日誌

[08/24 03:35:39] [DEBUG] RuleBasedTransactionAttribute: Applying rules to determine whether transaction should rollback on usertest.exception.BusinessException: Error
[08/24 03:35:39] [DEBUG] RuleBasedTransactionAttribute: Winning rollback rule is: RollbackRuleAttribute with pattern [BusinessException]
[08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Triggering beforeCompletion synchronization
[08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Initiating transaction rollback
[08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@1db05b2]
[08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Triggering afterCompletion synchronization
[08/24 03:35:39] [DEBUG] TransactionSynchronizationManager: Clearing transaction synchronization
[08/24 03:35:39] [DEBUG] TransactionSynchronizationManager: Removed value [org.springframework.jdbc.datasource.ConnectionHolder@1833eca] for key [org.apache.commons.dbcp.BasicDataSource@4aa0ce] from thread [main]
[08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection@1db05b2] after transaction
[08/24 03:35:39] [DEBUG] DataSourceUtils: Returning JDBC Connection to DataSource




[08/24 03:39:16] [DEBUG] TransactionInterceptor: Completing transaction for [usertest.dao.UsersDAO.testInsertAndUpdate] after exception: java.lang.Exception: Error
[08/24 03:39:16] [DEBUG] RuleBasedTransactionAttribute: Applying rules to determine whether transaction should rollback on java.lang.Exception: Error
[08/24 03:39:16] [DEBUG] RuleBasedTransactionAttribute: Winning rollback rule is: null
[08/24 03:39:16] [DEBUG] RuleBasedTransactionAttribute: No relevant rollback rule found: applying superclass default
[08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Triggering beforeCommit synchronization
[08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Triggering beforeCompletion synchronization
[08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Initiating transaction commit
[08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@1db05b2]
[08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Triggering afterCommit synchronization


[08/24 03:41:40] [DEBUG] TransactionInterceptor: Completing transaction for [usertest.dao.UsersDAO.testInsertAndUpdate] after exception: usertest.exception.BusinessException: Error
[08/24 03:41:40] [DEBUG] RuleBasedTransactionAttribute: Applying rules to determine whether transaction should rollback on usertest.exception.BusinessException: Error
[08/24 03:41:40] [DEBUG] RuleBasedTransactionAttribute: Winning rollback rule is: RollbackRuleAttribute with pattern [Exception]
[08/24 03:41:40] [DEBUG] DataSourceTransactionManager: Triggering beforeCompletion synchronization
[08/24 03:41:40] [DEBUG] DataSourceTransactionManager: Initiating transaction rollback
[08/24 03:41:40] [DEBUG] DataSourceTransactionManager: Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@1db05b2]
[08/24 03:41:40] [DEBUG] DataSourceTransactionManager: Triggering afterCompletion synchronization
[08/24 03:41:40] [DEBUG] TransactionSynchronizationManager: Clearing transaction synchronization




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章