事务的传播行为是针对嵌套事务而言。即是针对(需要事务的业务方法)调用(需要事务的业务方法)。
** 注意事项:
以下案例的业务方法在不同的类下:
在同一个类下面不同的方法就算都有事务,调用的方法事务都是不会生效的。
意思是在A类的a方法调用b方法,a,b方法均有事务,直接调用b方法,事务是不会生效的。
原因是spring事务处理实际是基于动态代理生成类进行事务管理的,而直接调用b方法,调用的实际是当前类的b方法,而并非是代理类的方法,所以b方法并不能加入事务处理。**
解决方法有很多:
①:可以在当前类注入被spring管理的bean
之前直接调用
this.b()
之后先注入 A类, 最后以a.b()方式调用,事务即可生效。
@Autowired
private A a;
a.b()
②:利用aop获取当前的代理对象
AopContext.currentProxy()
回归正题。。。
@Transactional
spring的默认传播行为。
作用:
** 支持事务,如果业务方法执行时在一个事务中,则加入当前事务,否则则重新开始一个事务。
外层事务提交了,内层才会提交。
内/外只要有报错,他俩会一起回滚。(栗子二,三)
只要内层方法报错抛出异常,即使外层有try-catch,该事务也会回滚!(栗子一)
内层不存在事务,外层存在事务,即加入外层的事务,不管内层,外层报错,都会回滚事务。**
栗子一:
条件:外层正常try-catch内层,内层出错。
结果:事务回滚,内层外层都回滚。
报错:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
外层方法
//外层正常
@Service
public class UserServiceImpl implements UserService{
@Autowired
private TeacherService teacherService;
@Override
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
try {
teacherService.add();
}catch (Exception e){
e.printStackTrace();
}
return 0;
}
}
内层方法
//内层出错
@Service
public class TeacherServiceImpl implements TeacherService {
@Autowired
private TeacherMapper teacherMapper;
@Override
@Transactional
public int add() {
teacherMapper.add(1, 10);
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
}
栗子二:
条件:外层正常,内层出错,外层不try-catch
结果:事务回滚,内层外层都回滚。
栗子三:
条件:外层出错,内层正常
结果:事务回滚,内层外层都回滚。
作用:
** 支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。
内层事务结束,内层就提交了,不用等着外层一起提交。
外层报错回滚,不影响内层。(栗子一)
内层报错回滚,外层try-catch内层的异常,外层不会回滚。(栗子二)
内层报错回滚,然后又会抛出异常,外层如果没有捕获处理内层抛出来的这个异常,外层还是会回滚的。(栗子三)**
栗子一:
内层正常,外层报错。
结果:内层提交,外层回滚。
//外层报错
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
teacherService.add();
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
//内层正常
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int add() {
teacherMapper.add(1, 10);
return 0;
}
栗子二:
内层报错,外层try-catch。
结果:外层提交,内层回滚。
栗子三:
内层报错,外层不try-catch。
结果:外层回滚,内层回滚。
作用:
** 支持事务。如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。
内层事务结束,要等着外层一起提交。
外层回滚,内层也回滚。(栗子一)
如果只是内层回滚,影响外层。(栗子二)[因为默认成为了子事务]
如果只是内层回滚,外层try-catch内层的异常,不影响外层。(栗子三)
这个内层回滚不影响外层的特性是有前提的,否则内外都回滚。**
前提:
**1.JDK版本要在1.4以上,有java.sql.Savepoint。因为nested就是用savepoint来实现的。
2.事务管理器的nestedTransactionAllowed属性为true。
3.外层try-catch内层的异常。
**
栗子一:
内层正常,外层报错。
结果:内层回滚,外层回滚。
//外层报错
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
teacherService.add();
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
//内层正常
@Transactional(propagation = Propagation.NESTED)
public int add() {
teacherMapper.add(1, 10);
return 0;
}
栗子二:
内层报错,外层正常。
结果:内层回滚,外层回滚。
栗子三:
内层报错,外层正常try /catch 内层。
结果:内层回滚,外层提交。
//外层正常
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
try {
teacherService.add();
}catch (Exception e){
}
return 0;
}
//内层报错
@Transactional(propagation = Propagation.NESTED)
public int add() {
teacherMapper.add(1, 10);
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
事务管理器配置
@Configuration
public class TransactionConfig {
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
dataSourceTransactionManager.setNestedTransactionAllowed(true);
return dataSourceTransactionManager;
}
}
作用:
** 支持事务。当前有事务就加入当前事务。当前没有事务就算了,不会开启一个事物。**
栗子一:
外层正常有事务,内层报错。
结果:外层回滚,内层回滚。
//外层正常
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
teacherService.add();
return 0;
}
//内层报错
@Transactional(propagation = Propagation.SUPPORTS)
public int add() {
teacherMapper.add(1, 10);
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
栗子二:
外层正常有事务try/catch,内层报错。
结果:外层回滚,内层回滚。
栗子三:
外层报错有事务,内层正常。
结果:外层回滚,内层回滚。
栗子四:
外层正常无事务,内层报错。
结果:外层提交,内层提交。
作用:
** 支持事务,如果业务方法执行时已经在一个事务中,则加入当前事务。否则抛出异常。**
栗子一:
外层正常有事务,内层报错。
结果:外层回滚,内层回滚。
//外层正常
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
teacherService.add();
return 0;
}
//内层报错
@Transactional(propagation = Propagation.MANDATORY)
public int add() {
teacherMapper.add(1, 10);
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
栗子二:
外层正常无事务,内层报错。
结果:外层提交,内层回滚。
报错
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
作用:
** 不支持事务,如果业务方法执行时已经在一个事务中,则挂起当前事务,等方法执行完毕后,事务恢复进行。**
栗子一:
外层正常有事务,内层报错。
结果:外层回滚,内层提交。
//外层正常
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
teacherService.add();
return 0;
}
//内层报错
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int add() {
teacherMapper.add(1, 10);
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
栗子二:
外层正常有事务try/catch内层,内层报错。
结果:外层提交,内层提交。
//外层正常
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
try {
teacherService.add();
}catch (Exception e){
}
return 0;
}
//内层报错
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int add() {
teacherMapper.add(1, 10);
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
作用:
** 不支持事务。如果当前已经在一个事务中了,抛出异常。数据回滚。**
栗子一:
外层正常有事务,内层报错。
结果:外层回滚,内层回滚。
//外层正常
@Transactional(rollbackFor = {Exception.class})
public int transaction() {
userMapper.add(3, 200D);
teacherService.add();
return 0;
}
//内层报错
@Transactional(propagation = Propagation.NEVER)
public int add() {
teacherMapper.add(1, 10);
int x = 10; if(x == 10) throw new RuntimeException("出错啦!");
return 0;
}
报错
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
栗子二:
外层正常无事务,内层报错。
结果:外层提交,内层提交。
栗子三:
外层报错有事务,内层正常。
结果:外层回滚,内层回滚。