事务场景中,抛出异常被catch后,如果需要回滚,一定要手动回滚事务。
Spring事务框架默认只在抛出RuntimeException和unchecked exceptions时才将事务回滚(Errors默认 - 事务回滚),但是抛出的Checked exceptions时将不进行事务回滚。
如果我们希望改变这个默认情况,可以按场景做设置:
- 抛出checked exceptions时也回滚事务:@Transactional(rollbackFor=Exception.class)
- 抛出unchecked excepitons时不回滚事务:@Transactional(notRollbackFor=RunTimeException.class)
- 不需要事务管理:@Transactional(propagation=Propagation.NOT_SUPPORTED)
注意:
如果异常被try{}catch{}了,事务就不回滚了,
如果想让事务回滚必须再往外抛try{}catch{throw Exception}。
补充
- Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。
- @Transactional 注解标识的方法,处理过程尽量的简单。尤其是带锁的事务方法,能不放在事务里面的最好不要放在事务里面。可以将常规的数据库查询操作放在事务前面进行,而事务内进行增、删、改、加锁查询等操作。
@Transactional 实现原理:
@Transactional 实质是使用了 JDBC 的事务来进行事务控制的
@Transactional 基于 Spring 的动态代理的机制
1) 事务开始时,通过AOP机制,生成一个代理connection对象,
并将其放入 DataSource 实例的某个与 DataSourceTransactionManager 相关的某处容器中。
在接下来的整个事务中,客户代码都应该使用该 connection 连接数据库,
执行所有数据库命令。
[不使用该 connection 连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]
(物理连接 connection 逻辑上新建一个会话session;
DataSource 与 TransactionManager 配置相同的数据源)
2) 事务结束时,回滚在第1步骤中得到的代理 connection 对象上执行的数据库命令,
然后关闭该代理 connection 对象。
(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)
事务的隔离级别:是指若干个并发的事务之间的隔离程度
1. @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读,不可重复读) 基本不使用2. @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)3. @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)4. @Transactional(isolation = Isolation.SERIALIZABLE):串行化
事务的注解属性的说明:
propagation:事务传播行为。
isolation:事务隔离级别。
readOnly:事务的读写性,boolean型。
timeout:超时时间,int型,以秒为单位。
rollbackFor:一组异常类,遇到时回滚。(rollbackFor={SQLException.class})。
rollbackForCalssName:一组异常类名,遇到回滚,类型为string[]。
noRollbackFor:一组异常类,遇到不回滚。