之前一直看spring事务传播特性,搞不明白何为传播特性,最近仔细找了些资料研究发现,传播特性其实就是多个事务方法相互调用时,事务如何在这些方法间传播。
Spring 支持 7 种事务传播行为:
- PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
- PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。
Demo说明(来自http://blog.csdn.net/liovey/article/details/14149137)
采用Junit4.10.0+Spring3.2.1+Spring JDBCTemplate,通过注解方式配置事务,代码层次包括主测试类,两个Service对象,事务在Service开启。
概念
本地事务
数据库事务,默认事务为自动提交,因此如果一个业务逻辑类中有多次数据库操作将无法保证事务的一致性。
Spring事务
对本地事务操作的一次封装,相当于把使用JDBC代码开启、提交、回滚事务进行了封装。
上述两个概念会在demo中用到,以方便大家理解代码。
如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。需要JDBC3.0以上支持。
实例Demo
Propagation.REQUIRED
测试入口代码
[java] view plain copy
- <span style="font-size:14px">//会开启事务,在事务范围内使用则使用同一个事务,否则开启新事务
- @Test
- public void testRequires(){
- sService.addStudent();
- }</span>
Service代码
[java] view plain copy
- @Transactional(propagation = Propagation.REQUIRED)
- public void addStudent(){
- <span style="white-space:pre"> </span>String sql = "insert into student(name) values(‘st0‘)";
- jdbcTemplate.execute(sql);
- tService.addTeacher();
- throw new RuntimeException();
- }
[java] view plain copy
- @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
测试入口代码
[java] view plain copy
- //无论何时自身都会开启事务
- @Test
- public void testRequiresNew(){
- sService.addStudent5();
- }
Service代码
[java] view plain copy
- @Transactional(propagation = Propagation.REQUIRES_NEW)
- public void addStudent5(){
- String sql = "insert into student(name) values(‘st5‘)";
- jdbcTemplate.execute(sql);
- tService.addTeacher5();
- throw new RuntimeException();
- }
[java] view plain copy
- @Transactional(propagation = Propagation.REQUIRES_NEW)
- public void addTeacher5(){
- String sql = "insert into teacher(name) values (‘t5‘)";
- jdbcTemplate.execute(sql);
- }
经测试如果在addStudent5中抛出异常,学生数据不能正确提交,教师信息被正确提交。说明sService和tService是在两个独立的事务中运行,并且只要方法被调用就开启事务。
Propagation.SUPPORTS
测试入口代码
[java] view plain copy
- //自身不会开启事务,在事务范围内则使用相同事务,否则不使用事务
- @Test
- public void testSupport(){
- sService.addStudent6();
- }
Service代码
[java] view plain copy
- @Transactional(propagation = Propagation.SUPPORTS)
- public void addStudent6(){
- String sql = "insert into student(name) values(‘st6‘)";
- jdbcTemplate.execute(sql);
- tService.addTeacher6();
- throw new RuntimeException();
- }
[java] view plain copy
- @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
测试入口代码
[java] view plain copy
- //自身不会开启事务,在事务范围内使用挂起事务,运行完毕恢复事务
- @Test
- public void testNotSupport(){
- sService.addStudent4();
- }
Service代码
[java] view plain copy
- @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管理和开启事务,而是使用了本地事务,由于本地事务默认自动提交因此数据都提交成功。
测试入口代码
[java] view plain copy
- //自身不会开启事务,在事务范围内使用挂起事务,运行完毕恢复事务
- @Test
- public void testNotSupport1(){
- sService.addStudent();
- }
Service代码
[java] view plain copy
- @Transactional(propagation = Propagation.REQUIRED)
- public void addStudent(){
- String sql = "insert into student(name) values(‘st0‘)";
- jdbcTemplate.execute(sql);
- tService.addTeacher4();
- }
[java] view plain copy
- @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
测试入口代码
[java] view plain copy
- //自身不开启事务,必须在事务环境使用否则报错
- @Test
- public void testMandatory(){
- sService.addStudent1();
- }
Service代码
[java] view plain copy
- @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
测试入口代码
[java] view plain copy
- <span style="font-size:14px">//自身不会开启事务,在事务范围使用抛出异常
- @Test
- public void testNever(){
- sService.addStudent();
- }</span>
Service代码
[java] view plain copy
- <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>
[java] view plain copy
- <span style="font-size:14px">@Transactional(propagation = Propagation.NEVER)
- public void addTeacher3(){
- String sql = "insert into teacher(name) values (‘t3‘)";
- jdbcTemplate.execute(sql);
- }</span><span style="font-size:18px">
- </span>
经测试代码报错,由于sService开启了事务,当调用sService方法时由于其传播特性为never,因此报存在事务错误。
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never‘
Propagation.NESTED
测试入口代码
[java] view plain copy
- //如果没有事务环境其特性同Propagation.REQUIRED,否则嵌套运行事务
- @Test
- public void testNested(){
- sService.addStudent2();
- }
Service代码
[java] view plain copy
- @Transactional(propagation = Propagation.NESTED)
- public void addStudent2(){
- String sql = "insert into student(name) values(‘st2‘)";
- jdbcTemplate.execute(sql);
- tService.addTeacher2();
- throw new RuntimeException();
- }
[java] view plain copy
- @Transactional(propagation = Propagation.NESTED)
- public void addTeacher2(){
- String sql = "insert into teacher(name) values (‘t2‘)";
- jdbcTemplate.execute(sql);
- }
经测试代码报错,教师数据和学生数据都没有提交成功。说明其按照REQUIRED特性运行。对于嵌套事务,大家可以模拟两个数据源,一方的失败不会影响另一方。
以上是所有demo解析。完整的测试代码请在:Spring事务传播特性下载。
另:
大家感兴趣SpringAOP入门和原理,可以在SpringAOP下载。