@Transactional注解
在springboot中,在方法或类上添加@Transactional注解,将会把该方法或类中的数据库操作放到一个事务中,中途发生异常时,则事务会回滚,代码如下
@Transactional
public void test(){
//数据库操作1
//数据库操作2
//数据库操作3
//数据库操作4
}
@Transactional参数如下
参数 | 说明 |
---|---|
rollbackFor | 指定事务回滚的异常类型,可以指定一个或多个异常类型的数组,当方法抛出指定异常或其子类时,事务将回滚。 |
noRollbackFor | 指定不触发事务回滚的异常类型。可以设置一个或多个异常类型的数组。当方法中抛出指定的异常或其子类时,事务不会回滚。 |
timeout | 指定事务的超时时间,单位为秒,默认值为-1,表示没有超时限制。 |
readOnly | 指定事务是否为只读事务,默认值为false。如果设置为true,表示事务只涉及读操作,可以优化事务管理的性能。 |
isolation | 指定事务的隔离级别,默认值为Isolation.DEFAULT。可以使用Isolation枚举类中的不同选项来设置隔离级别,如DEFAULT、READ_UNCOMMITTED、READ_COMMITTED等,事务隔离级别请参考数据库事务 |
Propagation | 指定事务的传播行为,默认值为Propagation.REQUIRED。可以使用Propagation枚举类中的不同选项来设置传播行为,如REQUIRED、REQUIRES_NEW、SUPPORT |
事务的传播性
- REQUIRED:如果当前存在事务,则在该事务中执行方法;如果没有事务,则创建一个新的事务。这是最常用的传播性,方法调用将在同一个事务中执行。
- REQUIRES_NEW:每次都创建一个新的事务,并挂起当前事务(如果存在)。新事务与外部事务完全独立,方法调用将在新的事务中执行。
- SUPPORTS:如果当前存在事务,则在该事务中执行方法;如果没有事务,则以非事务的方式执行。方法调用不强制要求在事务中执行。
- NOT_SUPPORTED:以非事务的方式执行方法。如果当前存在事务,则将其挂起,方法调用不在事务中执行。
- MANDATORY:要求当前存在事务,并在该事务中执行方法。如果没有事务,则抛出异常。
- NEVER:禁止在事务中执行方法。如果当前存在事务,则抛出异常。
- NESTED:如果当前存在事务,则在嵌套事务中执行方法。如果没有事务,则创建一个新的事务。嵌套事务可以独立提交或回滚,但它受外部事务的影响。
事务避坑
-
自调用方法:如果一个带有事务的方法在同一个类中通过普通的方法调用自己,事务传播可能会失效。这是因为Spring会基于代理机制来管理事务,而自调用方法不会经过代理对象。为了避免这个问题,可以将自调用的方法抽取到另一个类中,并通过依赖注入的方式调用。
-
异步方法:在异步方法中,默认情况下,事务不会被传播到异步方法中。这是因为异步方法会在新的线程中执行,而事务是与当前线程绑定的。要在异步方法中启用事务传播,可以使用
@Async+
@Transactional注解来继承父线程的事务上下文
。 -
不同的异常处理:事务的回滚通常是基于抛出的异常类型来决定的。如果在事务内部捕获了异常并处理了它,那么事务可能不会回滚,除非在捕获的异常处理中手动抛出一个与事务回滚相关的异常。
-
多个事务注解:在同一个方法上同时使用多个事务注解(如
@Transactional
和@TransactionAttribute
)可能导致事务的行为不可预测。应该避免在同一个方法上使用多个事务注解,以免出现冲突和不一致的事务行为。 -
外部调用:如果一个有事务的方法被另一个没有事务的方法直接调用,事务传播可能会受到影响。在这种情况下,被调用的方法会在自己的事务中执行,而不是加入到外部方法的事务中。要确保事务传播正常工作,应该通过代理对象来调用有事务的方法。
-
外部类调用:如果一个有事务的方法被另一个类的方法调用,事务传播可能会受到影响。如果被调用的方法是通过依赖注入的方式注入到外部类中的,那么事务传播应该正常工作。但是,如果被调用的方法是通过直接实例化的方式创建的对象,并且不经过Spring的代理对象,事务传播可能会失效。
异步调用事务传播代码如下:
@Async
@Transactional
public void yourAsyncTransactionalMethod() {
// 异步事务处理
}
外部调用通过代理对象调用有事务的方法代码:
@Service
public class YourService {
@Transactional
public void yourTransactionalMethod() {
// 事务处理
}
}
public class OuterClass {
public void outerMethod() {
YourService targetObject = (YourService) AopProxyUtils.getSingletonTarget(yourService);
targetObject.yourTransactionalMethod(); // 通过目标对象调用有事务的方法
}
}