编程使用Spring事务管理,图解事务传播行为

原创 2012年03月31日 09:56:46

9.3.5  事务属性

       事务属性通过TransactionDefinition接口实现定义,主要有事务隔离级别、事务传播行为、事务超时时间、事务是否只读。

       Spring提供TransactionDefinition接口默认实现DefaultTransactionDefinition,可以通过该实现类指定这些事务属性。

  • 事务隔离级别:用来解决并发事务时出现的问题,其使用TransactionDefinition中的静态变量来指定:

         ISOLATION_DEFAULT:默认隔离级别,即使用底层数据库默认的隔离级别;

         ISOLATION_READ_UNCOMMITTED:未提交读;

         ISOLATION_READ_COMMITTED:提交读,一般情况下我们使用这个;

         ISOLATION_REPEATABLE_READ:可重复读;

         ISOLATION_SERIALIZABLE:序列化。

 

可以使用DefaultTransactionDefinition类的setIsolationLevel(TransactionDefinition. ISOLATION_READ_COMMITTED)来指定隔离级别,其中此处表示隔离级别为提交读,也可以使用或setIsolationLevelName(“ISOLATION_READ_COMMITTED”)方式指定,其中参数就是隔离级别静态变量的名字,但不推荐这种方式。

  • 事务传播行为:Spring管理的事务是逻辑事务,而且物理事务和逻辑事务最大差别就在于事务传播行为,事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的,Spring共支持7种传播行为:

 

 

Required:必须有逻辑事务,否则新建一个事务,使用PROPAGATION_REQUIRED指定,表示如果当前存在一个逻辑事务,则加入该逻辑事务,否则将新建一个逻辑事务,如图9-2和9-3所示;

 

图9-2 Required传播行为

 

图9-3 Required传播行为抛出异常情况

              在前边示例中就是使用的Required传播行为:

一、在调用userService对象的save方法时,此方法用的是Required传播行为且此时Spring事务管理器发现还没开启逻辑事务,因此Spring管理器觉得开启逻辑事务,

二、在此逻辑事务中调用了addressService对象的save方法,而在save方法中发现同样用的是Required传播行为,因此使用该已经存在的逻辑事务;

三、在返回到addressService对象的save方法,当事务模板类执行完毕,此时提交并关闭事务。

       因此userService对象的save方法和addressService的save方法属于同一个物理事务,如果发生回滚,则两者都回滚。

 

 

接下来测试一下该传播行为如何执行吧:

一、正确提交测试,如上一节的测试,在此不再演示;

二、回滚测试,修改AddressServiceImpl的save方法片段:

 

java代码:
Java代码  收藏代码
  1. addressDao.save(address);  

 

 

java代码:
Java代码  收藏代码
  1. addressDao.save(address);  
  2. //抛出异常,将标识当前事务需要回滚  
  3. throw new RuntimeException();  

 

二、修改UserServiceImpl的save方法片段:

 

java代码:
Java代码  收藏代码
  1. addressService.save(user.getAddress());  

 

 

java代码:
Java代码  收藏代码
  1. try {  
  2.     addressService.save(user.getAddress());//将在同一个事务内执行  
  3. catch (RuntimeException e) {  
  4. }  
  5.    

 

如果该业务方法执行时事务被标记为回滚,则不管在此是否捕获该异常都将发生回滚,因为处于同一逻辑事务。

 

三、修改测试方法片段:

 

java代码:
Java代码  收藏代码
  1. userService.save(user);  
  2. Assert.assertEquals(1, userService.countAll());  
  3. Assert.assertEquals(1, addressService.countAll());  

 

为如下形式:

 

 

java代码:
Java代码  收藏代码
  1. try {  
  2.     userService.save(user);  
  3.     Assert.fail();  
  4. catch (RuntimeException e) {  
  5. }  
  6. Assert.assertEquals(0, userService.countAll());  
  7. Assert.assertEquals(0, addressService.countAll());  

 

Assert断言中countAll方法都返回0,说明事务回滚了,即说明两个业务方法属于同一个物理事务,即使在userService对象的save方法中将异常捕获,由于addressService对象的save方法抛出异常,即事务管理器将自动标识当前事务为需要回滚。

 

 

 

 

RequiresNew:创建新的逻辑事务,使用PROPAGATION_REQUIRES_NEW指定,表示每次都创建新的逻辑事务(物理事务也是不同的)如图9-4和9-5所示:

 

图9-4 RequiresNew传播行为

 

图9-5 RequiresNew传播行为并抛出异常

接下来测试一个该传播行为如何执行吧:

1、将如下获取事务模板方式

 

java代码:
Java代码  收藏代码
  1. TransactionTemplate transactionTemplate = TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);  

 

       替换为如下形式,表示传播行为为RequiresNew:

 

java代码:
Java代码  收藏代码
  1. TransactionTemplate transactionTemplate = TransactionTemplateUtils.getTransactionTemplate(  
  2.         txManager,   
  3.         TransactionDefinition.PROPAGATION_REQUIRES_NEW,   
  4.         TransactionDefinition.ISOLATION_READ_COMMITTED);  

 

2、执行如下测试,发现执行结果是正确的:

 

java代码:
Java代码  收藏代码
  1. userService.save(user);  
  2. Assert.assertEquals(1, userService.countAll());  
  3. Assert.assertEquals(1, addressService.countAll());  

 

3、修改UserServiceImpl的save方法片段

 

java代码:
Java代码  收藏代码
  1. userDao.save(user);         
  2. user.getAddress().setUserId(user.getId());  
  3. addressService.save(user.getAddress());  

 

为如下形式,表示userServiceImpl类的save方法将发生回滚,而AddressServiceImpl类的方法由于在抛出异常前执行,将成功提交事务到数据库:

 

 

java代码:
Java代码  收藏代码
  1. userDao.save(user);         
  2. user.getAddress().setUserId(user.getId());  
  3. addressService.save(user.getAddress());  
  4. throw new RuntimeException();  

 

4、修改测试方法片段:

 

java代码:
Java代码  收藏代码
  1. userService.save(user);  
  2. Assert.assertEquals(1, userService.countAll());  
  3. Assert.assertEquals(1, addressService.countAll());  

 

为如下形式:

 

 

java代码:
Java代码  收藏代码
  1. try {  
  2.     userService.save(user);  
  3.     Assert.fail();  
  4. catch (RuntimeException e) {  
  5. }  
  6. Assert.assertEquals(0, userService.countAll());  
  7. Assert.assertEquals(1, addressService.countAll());  

 

Assert断言中调用userService对象countAll方法返回0,说明该逻辑事务作用域回滚,而调用addressService对象的countAll方法返回1,说明该逻辑事务作用域正确提交。因此这是不正确的行为,因为用户和地址应该是一一对应的,不应该发生这种情况,因此此处正确的传播行为应该是Required。

 

该传播行为执行流程(正确提交情况):

一、当执行userService对象的save方法时,由于传播行为是RequiresNew,因此创建一个新的逻辑事务(物理事务也是不同的);

二、当执行到addressService对象的save方法时,由于传播行为是RequiresNew,因此首先暂停上一个逻辑事务并创建一个新的逻辑事务(物理事务也是不同的);

三、addressService对象的save方法执行完毕后,提交逻辑事务(并提交物理事务)并重新恢复上一个逻辑事务,继续执行userService对象的save方法内的操作;

四、最后userService对象的save方法执行完毕,提交逻辑事务(并提交物理事务);

五、userService对象的save方法和addressService对象的save方法不属于同一个逻辑事务且也不属于同一个物理事务。

 

 

 

 

Supports:支持当前事务,使用PROPAGATION_SUPPORTS指定,指如果当前存在逻辑事务,就加入到该逻辑事务,如果当前没有逻辑事务,就以非事务方式执行,如图9-6和9-7所示:

 

图9-6 Required+Supports传播行为

 

       图9-7 Supports+Supports传播行为

 

 

 

 

NotSupported:不支持事务,如果当前存在事务则暂停该事务,使用PROPAGATION_NOT_SUPPORTED指定,即以非事务方式执行,如果当前存在逻辑事务,就把当前事务暂停,以非事务方式执行,如图9-8和9-9所示:

 

       图9-8 Required+NotSupported传播行为

 

       图9-9 Supports+NotSupported传播行为

 

 

 

 

Mandatory:必须有事务,否则抛出异常,使用PROPAGATION_MANDATORY指定,使用当前事务执行,如果当前没有事务,则抛出异常(IllegalTransactionStateException),如图9-10和9-11所示:

 

 

 

       图9-10 Required+Mandatory传播行为

 

       图9-11 Supports+Mandatory传播行为

 

 

 

 

Never:不支持事务,如果当前存在是事务则抛出异常,使用PROPAGATION_NEVER指定,即以非事务方式执行,如果当前存在事务,则抛出异常(IllegalTransactionStateException),如图9-12和9-13所示:

 

       图9-12 Required+Never传播行为

 

       图9-13 Supports+Never传播行为

 

 

 

 

 

Nested:嵌套事务支持,使用PROPAGATION_NESTED指定,如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚,如图9-14和9-15所示:

 

       图9-14 Required+Nested传播行为

 

图9-15 Nested+Nested传播行为

 

 

 

Nested和RequiresNew的区别:

1、  RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务;

2、  Nested嵌套事务回滚或提交不会导致外部事务回滚或提交,但外部事务回滚将导致嵌套事务回滚,而 RequiresNew由于都是全新的事务,所以之间是无关联的;

3、  Nested使用JDBC 3的保存点实现,即如果使用低版本驱动将导致不支持嵌套事务。

使用嵌套事务,必须确保具体事务管理器实现的nestedTransactionAllowed属性为true,否则不支持嵌套事务,如DataSourceTransactionManager默认支持,而HibernateTransactionManager默认不支持,需要我们来开启。

对于事务传播行为我们只演示了Required和RequiresNew,其他传播行为类似,如果对这些事务传播行为不太会使用,请参考chapter9包下的TransactionTest测试类中的testPropagation方法,方法内有详细示例。

 

  • 事务超时:设置事务的超时时间,单位为秒,默认为-1表示使用底层事务的超时时间;

         使用如setTimeout(100)来设置超时时间,如果事务超时将抛出org.springframework.transaction.TransactionTimedOutException异常并将当前事务标记为应该回滚,即超时后事务被自动回滚;

         可以使用具体事务管理器实现的defaultTimeout属性设置默认的事务超时时间,如DataSourceTransactionManager. setDefaultTimeout(10)。

 

  • 事务只读:将事务标识为只读,只读事务不修改任何数据;

         对于JDBC只是简单的将连接设置为只读模式,对于更新将抛出异常;

         而对于一些其他ORM框架有一些优化作用,如在Hibernate中,Spring事务管理器将执行“session.setFlushMode(FlushMode.MANUAL)”即指定Hibernate会话在只读事务模式下不用尝试检测和同步持久对象的状态的更新。

         如果使用设置具体事务管理的validateExistingTransaction属性为true(默认false),将确保整个事务传播链都是只读或都不是只读,如图9-16是正确的事务只读设置,而图9-17是错误的事务只读设置:

 

图9-16 正确的事务只读设置

 

 

图9-17 错误的事务只读设置

如图10-17,对于错误的事务只读设置将抛出IllegalTransactionStateException异常,并伴随“Participating transaction with definition [……] is not marked as read-only……”信息,表示参与的事务只读属性设置错误。


spring 事务传播行为实例分析

欢迎转载和指正,转载时请说明来源
  • pml18710973036
  • pml18710973036
  • 2017年02月28日 17:29
  • 3351

Spring的7种事务传播行为类型

1、PROPAGATION_REQUIRED :如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。 2、PROPAGATION_SUPPORTS:支持当前事务,...
  • hsgao_water
  • hsgao_water
  • 2016年10月19日 16:50
  • 4673

spring 事务传播行为和事务隔离级别

1、Spring声明式事务 声明式事务(declarative transaction management)是Spring提供的对程序事务管理的方式之一。 Spring的声明式事务顾...
  • yujin753
  • yujin753
  • 2014年12月29日 20:30
  • 2702

使用Spring注解方式管理事务与传播行为详解

前面讲解了怎么使用@Transactional注解声明PersonServiceBean底下所有的业务方法需要事务管理,那么事务是如何来管理的呢? 我们知道当每个业务方法执行的时候,它都会打开事务,...
  • yerenyuan_pku
  • yerenyuan_pku
  • 2016年10月21日 16:48
  • 4155

浅析Spring 事务(二十一) spring事务的传播行为

经过我们上面一个章节的初步了解,我们已经知道了Spring事务的基本配置,今天我们一起接着讨论一下spring事务的传播...
  • linuu
  • linuu
  • 2016年03月29日 16:07
  • 2390

浅析Spring事务传播行为和隔离级别

7个传播行为,4个隔离级别。 Spring事务的传播行为和隔离级别[transaction behavior and isolated level] Spring中事务的定义: Propagation...
  • it_wangxiangpan
  • it_wangxiangpan
  • 2014年04月20日 13:23
  • 15092

Spring Boot中的事务管理

什么是事务? 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合。由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常,异常会导致后续操作无法完成,此时由于业务...
  • linzhiqiang0316
  • linzhiqiang0316
  • 2016年09月23日 14:41
  • 3397

Spring中事务传播行为种类

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为, 它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播: 事务传播行为类型 ...
  • huaishuming
  • huaishuming
  • 2015年09月16日 12:28
  • 1104

Spring7种事务传播行为类型

事务传播行为种类 Spring在TransactionDefinition接口中规定了7种类型的事务传播行为, 它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播: 事务传播行为类型 ...
  • liuwei063608
  • liuwei063608
  • 2012年07月25日 14:56
  • 3758

spring中事务和传播属性配置代码

对service层进行事物管理 1 配置事物管理器 2 配置事物 3 对com.nufront.euht.service下的包和子包进行AOP拦截...
  • jianfpeng241241
  • jianfpeng241241
  • 2016年08月02日 14:12
  • 245
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:编程使用Spring事务管理,图解事务传播行为
举报原因:
原因补充:

(最多只允许输入30个字)