天天看点

spring事务管理Propagation测试及疑惑

spring事务传播模式一共有七种,定义在org.springframework.transaction.annotation.Propagation(4.3.8.RELEASE)

REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED):

默认模式,官方解释如下:

当传播属性设置为

PROPAGATION_REQUIRED

时, 将会为设置应用到的每一个方法创建一个逻辑上的事务 作用域. 这每一个单独的逻辑事务作用域可以单独的确定回滚状态, 在逻辑上独立于事务范围的外部事务范围. 当然, 考虑到标准的

PROPAGATION_REQUIRED

的行为, 所有的这些作用域都将会映射到相同的物理事务上. 因此, 在内部事务作用域中作的事务回滚标记确实会影响到外部事物实际上提交的可能性(这和你所期待的一样).

然而, 在内部事务作用域中标记了回滚, 外部事物决定它自己不回滚的情况下, 这样的回滚(由内部事务作用域 静默触发)就不是期待的了. 一个对应的

UnexpectedRollbackException

将会在在那里抛出. 这是一个异常 行为, 所以事务的调用者将不可能会在事务其实没有提交的时候被误导为假设提交了. 所以对于内部事务作用域 (在外部调用者没有发觉时)静默的标记了回滚的情况下, 外部调用者调用了提交. 那么外部调用者需要收到一个 

UnexpectedRollbackException

来清楚的知道需要用一个回滚来取而代之(提交).

spring事务管理Propagation测试及疑惑

说了这么多,简而言之:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS):

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY):

方法必须在事务中运行,否则抛出异常

REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW):

spring事务管理Propagation测试及疑惑

相比较于

PROPAGATION_REQUIRED

PROPAGATION_REQUIRES_NEW

对每一个受影响的事务作用域都使用完全 独立的事务. 这样, 物理上的事务就不同了并且可以独立的提交或者回滚, 外部事物不会影响到内部事务的回滚 状态.简而言之就是如果方法运行在事务中,则挂起原事务,创建一个独立的新事物。否则也是创建一个独立的事务。

NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED):

以非事务方式运行,如果当前存在事务,则把当前事务挂起。

NEVER(TransactionDefinition.PROPAGATION_NEVER):

以非事务方式运行,如果当前存在事务,则抛出异常。

NESTED(TransactionDefinition.PROPAGATION_NESTED):

PROPAGATION_NESTED

对多个可以回滚到的保存点使用了一个单独的底层事务. 这种局部化的回滚允许一个 内部事务触发一个针对它的作用域的回滚, 尽管一些操作已经回滚了, 但外部事物还是可以继续物理上的事务. 这个设置通常都和JDBC的保存点对应, 所以只会在JDBC的资源的事务上有作用. 请查看Spring的

DataSourceTransactionManager

.

为了加深印象,做几个试验试一下这几个模式

@Service
public class TestServiceImpl implements TestService {

	@Autowired
	private TestMapper testMapper;

	@Override
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public int updateA() {
		testMapper.updateA();
		updateB();
		return 0;
	}

	@Override
	@Transactional
	public int updateB() {
		testMapper.updateB();
		System.out.println(10 / 0);
		return 0;
	}

	@Override
	public void test() {
		updateA();
	}
}
           

updateA更新表A中的字段,updateB更新表B中的字段

使用controller调用test方法时两个表的数据都变更了未回滚。

使用controller调用updateA时两个表的数据都未变更,也就是都回滚了。

如果将修改test方法加上事务注解如下

@Override
@Transactional
public void test() {
	updateA();
}
           

则使用controller调用test方法时两个表的数据都未变更了也就是回滚了。

这个不太明白,按理说执行updateA时已经创建了事务,updateB也运行在事务中,当updateB异常时都应该回滚,为什么会和test方法有关系呢?

接着往下测试

@Override
	@Transactional
	public int updateA() {
		testMapper.updateA();
		try{
			updateB();
		}catch(Exception e){
			
		}
		return 0;
	}

	@Override
	@Transactional
	public int updateB() {
		testMapper.updateB();
		System.out.println(10 / 0);
		return 0;
	}
           

controller调用updateA时两个表都更新了未回滚(为什么不是表A更新而表B回滚呢?),上边的代码删除try catch后两个表都不会更新也就是都会回滚。

继续测试

@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public int updateA() {
		testMapper.updateA();
		try{
			updateB();
		}catch(Exception e){
			
		}
		return 0;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public int updateB() {
		testMapper.updateB();
		System.out.println(10 / 0);
		return 0;
	}
           

controller调用updateA时依然是更新了两个表(我已经开始怀疑打开的方式有问题了),删除try catch以后依旧是两个表都回滚。

好吧,感觉是哪里出问题了,应该是propagation这个属性配置后没起到作用,试一下

@Override
	@Transactional(propagation=Propagation.NEVER)
	public int updateB() {
		testMapper.updateB();
//		System.out.println(10 / 0);
		return 0;
	}

	@Override
	@Transactional
	public void test() {
		updateA();
		try{
			updateB();
		}catch(Exception e){
			e.printStackTrace();
		}
		
	}
           

controller调用test方法运行正常,两个表数据更新,未打印任何异常。

又查了一下资料,参考http://blog.csdn.net/despair1989/article/details/40538099内外层方法不能在同一个类中,于是复制了一个新的接口和实现类重新测试,好吧终于可以了,以下是修改后的部分代码

@Service
public class TestServiceImpl implements TestService {

	@Autowired
	private TestMapper testMapper;
	@Autowired
	private Test2Service test2Service;

	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public int updateA() {
		testMapper.updateA();
		try{
			test2Service.updateB();
		}catch(Exception e){
			
		}
			
		return 0;
	}
...
}
           
@Service
public class Test2ServiceImpl implements Test2Service {

	@Autowired
	private TestMapper testMapper;

	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public int updateA() {
		testMapper.updateA();
		try{
			updateB();
		}catch(Exception e){
			
		}
			
		return 0;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public int updateB() {
		testMapper.updateB();
		System.out.println(10 / 0);
		return 0;
	}

	@Override
	@Transactional
	public void test() {
		
	}
}
           

当调用TestServiceImpl类的updateA方法时表A更新,表B回滚

最后说下过程中遇到的几个问题

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.tz.service.TestService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
           

原因及解决方法:出现这个问题是因为添加注解@Service时把它添加到接口类上了,删掉放到实现类就可以了

spring事务管理Propagation测试及疑惑

Caused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/util/DefaultIndenter
           

原因及解决方法:

配置json映射如下

<bean id="mappingJacksonHttpMessageConverter"
		class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
		<property name="supportedMediaTypes">
			<list>
				<value>text/html;charset=UTF-8</value>
			</list>
		</property>
	</bean>
           

配置的json相关pom如下,与上边的配置存在版本兼容的问题

<!-- json -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.4.2</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>2.4.2</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.4.2</version>
		</dependency>
           

修改相关json包版本为2.7.4就可以了

配置完以后,发现能修改数据但是注解@Transactional完全不起作用

原因及解决方法:扫描配置

<context:component-scan base-package="com.tz">
           

事务配置

<!-- 配置事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
		p:dataSource-ref="dataSource">
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager"
		proxy-target-class="true" />
           

以上配置完@Transactional不起作用,后参考 http://blog.csdn.net/will_awoke/article/details/12002705 修改扫描包的配置,修改后如下

spring-mvc.xml

<context:component-scan base-package="com.tz">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
	</context:component-scan>
           

spring-mybits.xml

<context:component-scan base-package="com.tz">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
	</context:component-scan>
           

ok,终于可以了