【手動事務】SpringMVC手動提交事務(聲明式事務)同時對多個表插入數據,異常全部回滾

之前關於Spring事務只是簡單的瞭解,直接在項目的applicationContext裏面直接配置了事務管理之後,就不用關心事務的提交了,spring會幫我們自動提交事務,在異常時直接拋出運行時異常了。

但是有時候在項目中需要一系列連續的操作,比如對多個表同時執行插入新數據的操作,在其中一個出現異常時,就全部回滾,這時原配置文件裏的的自動提交事務就不能符合需求了。

但是手動提交事務又不懂,又開始了查資料,在查了大半天資料之後,對spring的事務管理了解了點皮毛,這裏記錄一下。

剛開始在網上搜手動提交事務時,發現好多重複的文章,只是簡單的將代碼貼出來了,沒有必要的說明,對於我這種小白來說,理解不了,也不知道怎麼運用到項目中去,這裏將我自己的爬坑歷程記錄一下,只是簡單的實現了,手動控制事務提交,異常自動回滾,但是我直接將事務管理放在了controller的方法裏面,但是在翻了很多博客之後,發現很多人說事務要放在service層裏,但是放在service層裏面,如果我使用兩個service都進行插入數據的操作,這樣就只有一個回滾,另一個還是自動提交了,沒有實現需求中的異常時本次操作全部回滾的目的。

在這裏插入圖片描述

在剛開始時,我測試了一下自動提交事務,直接在其中一個service裏面加入一個異常

	public int insertSelective(User user) {
		
		int i = userDao.insertSelective(user);
		System.out.println("1111111");
		int a = 10/0;	// 拋出異常
		int i2 = userDao.insertSelective(user);
		if (i>0 && i2 > 0) {
			return 1;
		}
		return 0; 
	}

然後在controller裏面執行兩次插入操作:

		try {
			insertSelective2 = roleService.insertSelective(role);
			insertSelective = userService.insertSelective(user);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			System.out.println("出現異常");
		}

在debug時發現,控制檯可以正常打印1111,但是數據庫裏並沒有新的用戶信息,可見雖然執行了第一句插入用戶的操作,但是事務還沒有提交,插入數據的動作還沒完成,這時遇到了除數是0的異常,事務就直接回滾了,但是controller類裏的insertSelective2 = roleService.insertSelective(role);這一句仍然正常執行了,並且產生了新的數據。到這裏我才發現好像事務是在service的基礎上分開的,每個service有一個自己的事務,事務異常了,不會影響到別的service的業務。

後來又查資料之後,發現自己在applicationContext的配置文件裏面,配置了事務的切面了,就是以service爲切面配置的,這樣每個service就會有一個獨立的事務,事務的提交不會影響到別的已完成的事務。
配置如下:

	<!-- 配置事務切面 -->
	<aop:config>
		<aop:pointcut id="txPoint" expression="execution(* com.yang.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" />
	</aop:config>

這裏可以看到,事務是建立在service的層次上的,所以纔會在執行插入角色(role)信息之後不會因爲插入用戶信息(user)中間出現異常而回滾。

不知道我這樣理解的對不對,暫時先這麼理解了,貌似也說的通。

然後我又翻到博客裏直接說道聲明式事務的寫法,我對比我的測試項目裏的配置文件,嘗試在controller類裏試了一下,將兩次插入操作直接寫在手動開啓事務和提交事務之間,結果卻是正常回滾,後面的發生異常時,兩次插入操作都回滾了,實現了上面所說的功能需求。
applicationContext.xml配置文件中關於事務的部分爲:

	<!-- 配置事務管理 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!-- 配置事務通知屬性 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 定義事務傳播屬性 -->
		<tx:attributes>
			<tx:method name="insert*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="edit*" propagation="REQUIRED" />
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="new*" propagation="REQUIRED" />
			<tx:method name="set*" propagation="REQUIRED" />
			<tx:method name="remove*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
			<tx:method name="change*" propagation="REQUIRED" />
			<tx:method name="check*" propagation="REQUIRED" />
			<tx:method name="get*" propagation="REQUIRED" read-only="true" />
			<tx:method name="find*" propagation="REQUIRED" read-only="true" />
			<tx:method name="load*" propagation="REQUIRED" read-only="true" />
			<tx:method name="*" propagation="REQUIRED" read-only="true" />
		</tx:attributes>
	</tx:advice>

	<!-- 配置事務切面 -->
	<aop:config>
		<aop:pointcut id="txPoint" expression="execution(* com.yang.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" />
	</aop:config>

controller類裏的手動創建事務和手動提交事務內容:

	// 首先在controller類裏注入事務管理器,name的值爲配置文件裏的事務管理器的名稱
	@Resource(name = "transactionManager")
	private DataSourceTransactionManager transactionManager;

	// 測試方法
	@RequestMapping("/test")
	@ResponseBody
	public String test2() {
		DefaultTransactionDefinition transDefinition = new DefaultTransactionDefinition();
		// 開啓新事物
		transDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);// 事物隔離級別,開啓新事務
		TransactionStatus transStatus = transactionManager.getTransaction(transDefinition);// 獲得事務狀態
		User user = new User();
		user.setUsername("xiaoming");
		user.setPassword("111");
		user.setRoleId(3);

		TRole role = new TRole();
		role.setRolename("test");
		int insertSelective = 0;
		int insertSelective2 = 0;
		try {
			insertSelective2 = roleService.insertSelective(role);
			insertSelective = userService.insertSelective(user);
			transactionManager.commit(transStatus); 	// 提交事務
			System.out.println("result:" + insertSelective);
			System.out.println("result:" + insertSelective2);
		} catch (Exception e) {
			transactionManager.rollback(transStatus);	// 異常回滾
			return "false";
		}
		return "success";
	}

可以看到這裏將事務的提交和回滾清楚的列出來了,這裏手動創建開啓事務,然後執行插入操作,如果某一個插入操作出現了異常,直接進入catch裏面,執行異常回滾了,這樣就可以實現,這些不同表裏插入數據,一旦某一個插入出現了異常,都會事務都不會提交的問題了。

到這裏問題好像解決了,實現了對多個表操作,異常全部回滾的需求了。但是我看到有博客說事務最好是寫在service層裏,controller裏面只處理請求的轉發不進行業務的處理,但是沒有找到更好的解決辦法,也只能先這樣寫了。

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