Spring注解风格的事务传播机制

来源:互联网 发布:如何看淘宝消费总额 编辑:程序博客网 时间:2024/04/28 21:01

原文地址:http://blog.csdn.net/liovey/article/details/14149137

概念

本地事务

       数据库事务,默认事务为自动提交,因此如果一个业务逻辑类中有多次数据库操作将无法保证事务的一致性。


Spring事务

       对本地事务操作的一次封装,相当于把使用JDBC代码开启、提交、回滚事务进行了封装。

上述两个概念会在demo中用到,以方便大家理解代码。


传播特性

       该特性是保证事务是否开启,业务逻辑是否使用同一个事务的保证。当事务在传播过程中会受其影响。其传播特性包括:

  1、Propagation.REQUIRED

方法被调用时自动开启事务,在事务范围内使用则使用同一个事务,否则开启新事务。       

  2、Propagation.REQUIRES_NEW

无论何时自身都会开启事务

  3、Propagation.SUPPORTS

自身不会开启事务,在事务范围内则使用相同事务,否则不使用事务

  4、Propagation.NOT_SUPPORTED

自身不会开启事务,在事务范围内使用挂起事务,运行完毕恢复事务

  5、Propagation.MANDATORY

自身不开启事务,必须在事务环境使用否则报错

  6、Propagation.NEVER

自身不会开启事务,在事务范围使用抛出异常

  7、Propagation.NESTED

如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。需要JDBC3.0以上支持。


实例Demo

Propagation.REQUIRED

测试入口代码

@Test  public void testRequires(){      sService.addStudent();  }

Service代码

@Transactional(propagation = Propagation.REQUIRED)  public void addStudent(){  String sql = "insert into student(name) values('st0')";      jdbcTemplate.execute(sql);      tService.addTeacher();      throw new RuntimeException();  }

@Transactional(propagation = Propagation.REQUIRES)public void addTeacher(){        String sql = "insert into teacher(name) values ('t5')";        jdbcTemplate.execute(sql);}
经测试无论在tService还是sService如果不抛出异常,那么数据提交成功,如果抛出异常,数据提交失败。这说明tService和sService使用的是同一个事务,并且只要方法被调用就开启事务。


Propagation.REQUIRES_NEW

测试入口代码

//无论何时自身都会开启事务@Testpublic void testRequiresNew(){      sService.addStudent5();}

Service代码

@Transactional(propagation = Propagation.REQUIRES_NEW)public void addStudent5(){       String sql = "insert into student(name) values('st5')";       jdbcTemplate.execute(sql);       tService.addTeacher5();       throw new RuntimeException();}
@Transactional(propagation = Propagation.REQUIRES_NEW)public void addTeacher5(){       String sql = "insert into teacher(name) values ('t5')";       jdbcTemplate.execute(sql);}

经测试如果在addStudent5中抛出异常,学生数据不能正确提交,教师信息被正确提交。说明sService和tService是在两个独立的事务中运行,并且只要方法被调用就开启事务。


Propagation.SUPPORTS

测试入口代码

//自身不会开启事务,在事务范围内则使用相同事务,否则不使用事务@Testpublic void testSupport(){       sService.addStudent6();}       
Service代码
@Transactional(propagation = Propagation.SUPPORTS)public void addStudent6(){       String sql = "insert into student(name) values('st6')";       jdbcTemplate.execute(sql);       tService.addTeacher6();       throw new RuntimeException();}       
@Transactional(propagation = Propagation.SUPPORTS)public void addTeacher6(){       String sql = "insert into teacher(name) values ('t6')";       jdbcTemplate.execute(sql);}

经测试如果在addStudent6中抛出异常,学生数据和教师数据都被正确提交。说明sService和tService没有被spring管理和开启事务,而是使用了本地事务,由于本地事务默认自动提交因此数据都提交成功,但它们使用的却不是同一个事务,一旦出现异常将导致数据的不一致。


Propagation.NOT_SUPPORTED

测试入口代码

//自身不会开启事务,在事务范围内使用挂起事务,运行完毕恢复事务@Testpublic void testNotSupport(){       sService.addStudent4();}       
Service代码
@Transactional(propagation = Propagation.NOT_SUPPORTED)public void addStudent4(){       String sql = "insert into student(name) values('st4')";       jdbcTemplate.execute(sql);       throw new RuntimeException();}       
经测试如果在addStudent4中抛出异常,学生数据正确提交。说明sService没有被spring管理和开启事务,而是使用了本地事务,由于本地事务默认自动提交因此数据都提交成功。

测试入口代码

//自身不会开启事务,在事务范围内使用挂起事务,运行完毕恢复事务@Testpublic void testNotSupport1(){       sService.addStudent();}       
Service代码
@Transactional(propagation = Propagation.REQUIRED)public void addStudent(){       String sql = "insert into student(name) values('st0')";       jdbcTemplate.execute(sql);       tService.addTeacher4();}       
@Transactional(propagation = Propagation.NOT_SUPPORTED)public void addTeacher4(){       String sql = "insert into teacher(name) values ('t4')";       jdbcTemplate.execute(sql);       throw new RuntimeException();}       
经测试如果在addTeacher4中抛出异常,学生数据提交失败,教师数据提交成功。说明sService开启了事务,tService没有开启事务,而是使用了本地事务。

Propagation.MANDATORY

测试入口代码

//自身不开启事务,必须在事务环境使用否则报错@Testpublic void testMandatory(){       sService.addStudent1();}       
Service代码
@Transactional(propagation = Propagation.MANDATORY)public void addStudent1(){       String sql = "insert into student(name) values('st1')";       jdbcTemplate.execute(sql);}       

经测试代码报错。

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory',没有找到事务环境。


Propagation.NEVER

测试入口代码

//自身不会开启事务,在事务范围使用抛出异常@Testpublic void testNever(){       sService.addStudent();}
Service代码
<span style="font-size:14px">@Transactional(propagation = Propagation.REQUIRED)public void addStudent(){       String sql = "insert into student(name) values('st0')";       jdbcTemplate.execute(sql);       tService.addTeacher3();}</span>

@Transactional(propagation = Propagation.NEVER)public void addTeacher3(){       String sql = "insert into teacher(name) values ('t3')";       jdbcTemplate.execute(sql);}
经测试代码报错,由于sService开启了事务,当调用sService方法时由于其传播特性为never,因此报存在事务错误。

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'


Propagation.NESTED

测试入口代码

//如果没有事务环境其特性同Propagation.REQUIRED,否则嵌套运行事务@Testpublic void testNested(){       sService.addStudent2();}
Service代码
@Transactional(propagation = Propagation.NESTED)public void addStudent2(){       String sql = "insert into student(name) values('st2')";       jdbcTemplate.execute(sql);       tService.addTeacher2();       throw new RuntimeException();}
@Transactional(propagation = Propagation.NESTED)public void addTeacher2(){       String sql = "insert into teacher(name) values ('t2')";       jdbcTemplate.execute(sql);}

经测试代码报错,教师数据和学生数据都没有提交成功。说明其按照REQUIRED特性运行。对于嵌套事务,大家可以模拟两个数据源,一方的失败不会影响另一方。
原创粉丝点击