1. 程式人生 > >Spring的7種事務傳播行為型別

Spring的7種事務傳播行為型別

1、PROPAGATION_REQUIRED:如果當前沒有事務,就建立一個新事務,如果當前存在事務,就加入該事務,該設定是最常用的設定。

2、PROPAGATION_SUPPORTS:支援當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就以非事務執行。‘

3、PROPAGATION_MANDATORY:支援當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就丟擲異常。

4、PROPAGATION_REQUIRES_NEW:建立新事務,無論當前存不存在事務,都建立新事務。

5、PROPAGATION_NOT_SUPPORTED以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

6、PROPAGATION_NEVER以非事務方式執行,如果當前存在事務,則丟擲異常。

7、PROPAGATION_NESTED如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。

羅列了7種spring的事務傳播行為,我們具體來看看它的實現。在這裡,我們使用spring annotation註解實現事務。

實現事務的類BusinessServiceImpl

package com.aop;

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.entity.Student;

@Service("businessSerivce")
public class BusinessServiceImpl implements IBaseService {

	@Autowired
	IStudentDao studentDao;

	@Autowired
	IBaseServiceB baseServiceb;

	@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
	public String doA() throws Exception {
		Student st = new Student();
		st.setId(1);
		st.setSex("girl");
		st.setUsername("zx");
		studentDao.insertStudent(st);

		System.out.println(baseServiceb);
		System.out.println("是否是代理呼叫,AopUtils.isAopProxy(baseServiceb) : " + AopUtils.isAopProxy(baseServiceb));
		System.out
				.println("是否是cglib類代理呼叫,AopUtils.isCglibProxy(baseServiceb) : " + AopUtils.isCglibProxy(baseServiceb));
		System.out.println("是否是jdk動態介面代理呼叫,AopUtils.isJdkDynamicProxy(baseServiceb) : "
				+ AopUtils.isJdkDynamicProxy(baseServiceb));
		
		//使用代理呼叫方法doB()
		baseServiceb.doB();
		int i = 1 / 0;// 丟擲異常,doB()的事務事務回滾
		return "success";
	}

	// @Transactional(propagation = Propagation.REQUIRES_NEW, isolation =
	// Isolation.DEFAULT, rollbackFor = Exception.class)
	// public String doB() throws Exception {
	// Student st = new Student();
	// st.setId(2);
	// st.setSex("girl");
	// st.setUsername("zx2");
	// studentDao.insertStudent(st);
	//
	// return "success";
	// }

}

實現事務的類BusinessServiceImpl
package com.aop;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.entity.Student;

@Service("businessSerivceB")
public class BusinessServiceImplB implements IBaseServiceB {
  
	@Autowired
	IStudentDao studentDao;
	
	@Transactional(propagation = Propagation.REQUIRES_NEW,isolation=Isolation.DEFAULT,rollbackFor=Exception.class)  
	public String doB() throws Exception {
		Student st = new Student();
		st.setId(2);
		st.setSex("girl");
		st.setUsername("zx2");
		studentDao.insertStudent(st);
		return "success";
	}
	 
}

測試類:
package com.aop;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.thread.service.IBaseFacadeService;


//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(locations = { "classpath:/spring-ibatis.xml", "classpath:/spring-jdbctemplate.xml" })
public class TestStudentDao {

	public static void main(String[] args) {
		  try {
		  BeanFactory factory = new ClassPathXmlApplicationContext("spring-jdbctemplate.xml");
		  IBaseService service = (IBaseService) factory.getBean("businessSerivce");
		
			  service.doA();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

測試結果:

[email protected]
是否是代理呼叫,AopUtils.isAopProxy(baseServiceb) : true
是否是cglib類代理呼叫,AopUtils.isCglibProxy(baseServiceb) : true
是否是jdk動態介面代理呼叫,AopUtils.isJdkDynamicProxy(baseServiceb) : false
java.lang.ArithmeticException: / by zero


從測試結果可以看到,我們在資料庫中成功插入了doB()方法中要插入的資料,而doA()中插入的資料被回滾了,這是因為我們在doB()方法中加入ROPAGATION_REQUIRES_NEW傳播行為,doB()建立了屬於自己的事務,掛起了doA()中的事務,所以事務B提交了,而事務A因為執行異常插入的資料被回滾了。

如果我們將BusinessServiceImplB做下修改,改為第一種傳播行為ROPAGATION_REQUIRED

package com.aop;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.entity.Student;

@Service("businessSerivceB")
public class BusinessServiceImplB implements IBaseServiceB {
  
	@Autowired
	IStudentDao studentDao;
	
	@Transactional(propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT,rollbackFor=Exception.class)  
	public String doB() throws Exception {
		Student st = new Student();
		st.setId(2);
		st.setSex("girl");
		st.setUsername("zx2");
		studentDao.insertStudent(st);
		return "success";
	}
	 
}

測試結果:

[email protected]
是否是代理呼叫,AopUtils.isAopProxy(baseServiceb) : true
是否是cglib類代理呼叫,AopUtils.isCglibProxy(baseServiceb) : true
是否是jdk動態介面代理呼叫,AopUtils.isJdkDynamicProxy(baseServiceb) : false
java.lang.ArithmeticException: / by zero


從結果可以看到,我們沒有成功插入資料,這是為什麼呢?因為我們使用的是第一種傳播行為PROPAGATION_REQUIRED ,doB()方法被加入到doA()的事務中,doA()執行時丟擲了異常,因為doB()和doA()同屬於一個事務,則執行操作被一起回滾了。其實在doB()中我們不加入註解,也等同PROPAGATION_REQUIRED的效果。

接下來我們再來看看第五種傳播行為PROPAGATION_NOT_SUPPORTED,我們同樣修改B類,如下

package com.aop;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.entity.Student;

@Service("businessSerivceB")
public class BusinessServiceImplB implements IBaseServiceB {
  
	@Autowired
	IStudentDao studentDao;
	
	@Transactional(propagation = Propagation.NOT_SUPPORTED,isolation=Isolation.DEFAULT,rollbackFor=Exception.class)  
	public String doB() throws Exception {
		Student st = new Student();
		st.setId(2);
		st.setSex("girl");
		st.setUsername("zx2");
		studentDao.insertStudent(st);
		return "success";
	}
	 
}
測試結果:


可以看到doB()方法成功插入了資料。doA()方法中插入的資料被回滾了。這是因為傳播行為PROPAGATION_NOT_SUPPORTED是doB()以非事務執行的,並且提交了。所以當doA()的事務被回滾時,doB()的操作沒有被回滾。

其他的傳播行為就不一一列舉了,機制是差不多的。

注意:在上面的例項中,我沒有把doB()方法放在類BusinessServiceImpl,而是放在BusinessServiceImplB中,這是因為spring通過掃描所有含有註解的@Trasation的方法,使用aop形成事務增強advise。但是加入增強時是通過代理物件呼叫方法的形式加入的,如果將doB()方法放在doA()方法直接呼叫時,在呼叫doB()方法的時候是通過當前物件來呼叫doB()方法的,而不是通過代理來呼叫的doB()方法,這個時候doB()方法上加的事務註解就失效了不起作用。在Spring事務傳播行為在內部方法不起作用講到。

spring配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        	http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        	http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd"
        	default-lazy-init="false">

	<context:component-scan base-package="com.aop"/>
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	<!-- 資料 -->
	<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql://localhost:3306/lpWeb"/>
		<property name="username" value="root"/>
		<property name="password" value="root123"/>
	</bean>
	
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	 	<property name = "dataSource" ref="dataSource"/>  
	</bean>
  
	 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean> 
	
	<tx:annotation-driven transaction-manager="transactionManager" />  
</beans>