@Transactional事务的使用和注意事项及其属性

发布时间 2023-06-29 20:03:05作者: huigui_mint

一、注意事项

不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,不然注解可能无效。
不要将@Transactional放置在类级的声明中,放在类声明,会使得全部方法都有事务。所以@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,好比查询方法。不然对性能是有影响的。
使用了@Transactional的方法,对同一个类里面的方法调用, @Transactional无效。
好比有一个类Test,它的一个方法A,A再调用Test本类的方法B(无论B是否public仍是private),但A没有声明注解事务,而B有。则外部调用A以后,B的事务是不会起做用的。(常常在这里出错)

常见坑点1:遇到检查异常时,事务开启,也无法回滚。 例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚!

原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用 rollbackFor属性明确指定异常。

 

常见坑点2:在业务层捕捉异常后,发现事务不生效。 这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。

推荐做法:在业务层统一抛出异常,然后在控制层统一处理。

二、异常回滚效果

使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其余类调用才有效,故只能是public。道理和上面的有关联。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但事务无效。

抛出受检查异常XXXException,事务会回滚。

抛出运行时异常NullPointerException,事务会回滚。

在service中加上@Transactional,如果是直接调该方法,会回滚,如果是间接调用,不会回滚。(即上文3提到的)

在service中的private加上@Transactional,事务不会回滚。

注意点
Spring默认情况下会对(RuntimeException)及其子类来进行回滚,在遇见Exception及其子类的时候则不会进行回滚操作。
@Transactional既可以作用于接口,接口方法上以及类上,类的方法上。但是Spring官方不建议接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外,@Transactional注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在protected、private或者默认可见性的方法上使用@Transactional 注解,这将被忽略,也不会抛出任何异常。
Spring默认使用的是jdk自带的基于接口的代理,而没有使用基于类的CGLIB代理。
如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为:方法>实现类>接口;
建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制(基于接口的代理)是没问题;而使用CGLIB代理(继承)机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”;

三、@Transactional()添加属性

Exception异常
让Exception异常也进行回滚操作,在调用该方法前加上:
    @Transactional(rollbackFor = Exception.class)


让RuntimeException不进行回滚操作,在调用该方法前加上:
    @Transactional(noRollbackFor=RunTimeException.class)

只读事务
在整个方法运行前就不会开启事务:
这样就做成一个只读事务,可以提高效率
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

查看方法的事务是否已经被执行

TransactionSynchronizationManager.isActualTransactionActive() 

事务的传播及其属性的意义:

//如果有事务,那么加入事务,没有的话新创建一个
@Transactional(propagation=Propagation.REQUIRED)

//这个方法不开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)

//不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.REQUIREDS_NEW)

//必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.MANDATORY)

//不能在一个事务中执行,就是当前必须没有事务,否则抛出异常
@Transactional(propagation=Propagation.NEVER)

//其他bean调用这个方法,如果在其他bean中声明了事务,就是用事务。没有声明,就不用事务。
@Transactional(propagation=Propagation.SUPPORTS)

//如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动的事务,则按照REQUIRED属性执行,它使用一个单独的事务。这个书屋拥有多个回滚的保存点,内部事务的回滚不会对外部事务造成影响,它只对DataSource TransactionManager事务管理器起效。
@Transactional(propagation=Propagation.NESTED)

//只读,不能更新,删除
@Transactional(propagation=Propagation.REQUIRED,readOnly=true)

//超时30秒
@Transactional(propagation=Propagation.REQUIRED,timeout=30)

//数据库隔离级别
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)