Spring事務管理(一)-三種配置方式

當項目的數據須要持久化存儲時,不可避免要和數據庫交互。在交互過程當中,對事務的支持則是尤其重要。JDBC規範支持對事務的操做,在深刻淺出JDBC(一) - Connection與事務介紹一章中簡要介紹了JDBC事務相關的概念。JDBC將對不一樣數據庫的交互規範化,包括事務的操做,讓開發者能夠屏蔽不一樣數據庫的差別使用接口編程。但事務的開啓和關閉,以及事務的控制和配置仍是須要手動編碼控制,未免繁瑣且易出錯。Spring基於此之上,開放出一套事務管理機制,將開發者從繁瑣的事務控制中解脫出來,能夠便捷地執行事務控制。然而做爲開發者,便捷以後的原理也須要了解,才能更好地把控程序。接下來,我將從Spring事務管理的配置到原理逐步介紹其運行機制,本篇先介紹三種從原始到簡化的配置方式。mysql

以mybatis+mysql爲基礎,基本的xml配置以下spring

<!-- 數據源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
	<property name="url" value="jdbc:mysql://192.168.1.160:3306/lichao"></property>
	<property name="username" value="root"></property>
	<property name="password" value="root"></property>
	<property name="connectionProperties" value="useUnicode=true;autoReconnect=true;failOverReadOnly=false;characterEncoding=utf8;zeroDateTimeBehavior=convertToNull;allowMultiQueries=true"></property>
</bean>

<!-- mybatis的Session工廠 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="configLocation" value="mybatis-config.xml"></property>
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- UserMapper代理 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
	<property name="mapperInterface" value="com.lcifn.spring.transaction.mapper.UserMapper"/>
	<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>

<!-- userManager實例 -->
<bean id="userManager" class="com.lcifn.spring.transaction.manager.UserManager">
	<property name="userMapper" ref="userMapper"></property>
</bean>

這裏對mybatis的配置就不過多介紹了,事務定義在UserManager層,UserManager中定義一個批量操做的方法,來驗證事務。sql

@Slf4j
public class UserManager {
	
	@Getter
	@Setter
	private UserMapper userMapper;

	public void batchOperator(){
		User user = new User("lily", 25);
		// 插入一條user記錄
		int insertRows = userMapper.insert(user);
		if(insertRows > 0){
			user = userMapper.getUser(user.getId());
			log.info(user.toString());
			user.setName("jack");
			user.setAge(28);
			// 更新user記錄
			userMapper.update(user);
		}
	}
}

1.TransactionProxyFactoryBean代理

使用TransactionProxyFactoryBean直接代理UserManager數據庫

<!-- userManager事務代理類 -->
<bean id="userManagerTransactionProxy" 
	class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
	<!-- 原始對象 -->
	<property name="target" ref="userManager"/>
	<!-- 事務屬性 -->
	<property name="transactionAttributes">
		<props>
			<prop key="batchOperator">PROPAGATION_REQUIRED</prop>
		</props>
	</property>
	<!-- 事務管理器 -->
	<property name="transactionManager">
		<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
			<property name="dataSource" ref="dataSource"/>
		</bean>
	</property>
</bean>

TransactionProxyFactoryBean使用ProxyFactory生成代理類完成對事務的處理。transactionAttributes接收一個properties對象,key爲方法名,value爲方法對應的事務配置,配置規則的解析類是TransactionAttributeEditor,配置規則以下express

配置名稱 前綴 配置選項 默認配置
傳播屬性 PROPAGATION_ REQUIRED|MANDATORY|REQUIRES_NEW|NESTED等 REQUIRED
隔離級別 ISOLATION_ READ_UNCOMMITTED|READ_COMMITTED|REPEATABLE_READ|SERIALIZABLE DEFAULT
超時時間 timeout_ 單位:秒 -1
只讀 readOnly 不配置表示可讀可寫,配置了便可讀 可讀可寫
不回滾異常規則(noRollbackFor) + 異常類包路徑
回滾異常規則(rollbackFor - 異常類包路徑 運行時異常及Error

事務管理器則是PlatformTransactionManager的子類,實現其獲取事務,提交事務和回滾事務三個方法,這裏使用jdbc的事務管理器DataSourceTransactionManager。apache

寫一個測試類編程

public class TransactionProxyFactoryBeanTest {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("TransactionProxyFactoryBean.xml");
		UserManager userManager = context.getBean("userManagerTransactionProxy", UserManager.class);
		userManager.batchOperator();
	}
}

經過上下文獲取userManager的代理對象,並執行操做方法,能夠在日誌中看到事務的運行。mybatis

2.XML配置

上面的配置方式只能對一個類代理,用來研究Spring事務管理的機制很好,但在項目中,須要批量地配置事務管理。使用Spring AOP中的Advice和Pointcut的方式就能完成,並且Spring定義了tx:advice標籤來支持配置。app

<!-- 事務管理器 -->
<bean id="transactionManager"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 事務Advice加強配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="get*" read-only="true" />
		<tx:method name="*" propagation="REQUIRED"/>
	</tx:attributes>
</tx:advice>

<!-- 配置事務aop -->
<aop:config>
	<aop:pointcut id="userPointcut"
		expression="execution(* com.lcifn.spring.transaction.manager.UserManager.*(..))" />
	<aop:advisor advice-ref="txAdvice" pointcut-ref="userPointcut" />
</aop:config>

在tx:advice中,須要配置事務管理器,在其子標籤中tx:attributes中配置方法和事務配置的關係。方法配置支持通配符匹配多個方法,事務的配置同上面的基本一致,只是Spring提供了屬性標籤來方便配置。配置aop:pointcut切入點,決定哪些類哪些方法須要被代理。測試

寫一個測試類,這裏直接獲取userManager,由於aop:config自動代理的緣由,返回的已是代理對象了。

public class TransactionTxAdvice {
	
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("transaction-tx-advice.xml");
		UserManager userManager = context.getBean("userManager", UserManager.class);
		userManager.batchOperator();
	}
}

3.聲明式事務配置@Transactional

XML配置的方式已經能大大地節約配置操做,但若是要對每一個方法細粒度地配置,xml也會變得很繁瑣。怎麼辦?這時咱們想到了註解。Spring提供了超級便捷的方式,經過註解在類或方法上完成事務的控制和配置。

<!-- 事務註解驅動 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>

xml中的就是上面簡單的配置,而在類或方法上使用@Transactional註解,好比上面的batchOperator方法能夠配置以下

// 事務傳播屬性爲必須,超時時間爲2秒
@Transactional(propagation = Propagation.REQUIRED, timeout = 2)

每一個方法是否開啓事務,以及事務配置什麼屬性均可以經過這種方式精細化地控制。

4.事務隔離級別和傳播方式

Spring的事務隔離級別支持sql標準定義的四個級別:Read Uncommitted(未受權讀取),Read Committed(受權讀取),Repeatable Read(可重複讀取),Serializable(序列化)。可在深刻淺出JDBC(一) - Connection與事務介紹中瞭解詳細。

Spring定義了事務傳播方式,指的是多個層次(非同一個類)的事務方法執行時的規則,即事務在方法中的傳播方式。Spring定義了七種傳播行爲(包括事務的新建和回滾行爲):

  1. REQUIRED:默認傳播方式。若是當前有事務,支持當前事務;若是沒有事務,建立一個事務。回滾時所有回滾。
  2. REQUIRES_NEW:若是當前有事務,將當前事務掛起,建立新的事務;若是沒有事務,也建立新的事務。回滾時只回滾當前事務,不影響其餘事務。
  3. NESTED:嵌套事務。若是當前有事務,設置savepoint保存點;若是沒有事務,建立一個事務。回滾時只回滾到保存點。
  4. SUPPORTS:若是當前有事務,支持當前事務;若是沒有事務,就以非事務方式執行。回滾時所有回滾。
  5. MANDATORY:強制事務。若是當前有事務,支持當前事務;若是沒有事務,則拋出異常。回滾時所有回滾。
  6. NOT_SUPPORTED:若是當前有事務,將當前事務掛起,以非事務方式執行。若是沒有事務,以非事務方式執行。不存在回滾。
  7. NEVER:以非事務方式執行,若是當前存在事務,則拋出異常。不存在回滾。

對於事務的建立,有一點須要着重強調。JDBC默認鏈接的提交方式爲自動,若是開啓事務,即將自動提交改成手動。所以開啓一個新的事務,便是獲取一個新的鏈接, ,具體實現也會在以後的源碼解析裏提示。下一章咱們來解析TransactionProxyFactoryBean的實現原理。

相關文章
相關標籤/搜索