为什么要写这篇文章?
你是不是和我一样,每次想要在代码中使用事务回滚的时候就想起来有这么一个@Transacitonal注解,但是隐隐约约记得注解有好几种参数,不知道该选哪一种,也不知道什么地方应该添加这个注解。
于是乎,你和我一样打开了百度开始搜索@Transactional注解,如果是这样的话,建议收藏这篇文章,我会干练的罗列哪里用@Transactional、@Transactional不同参数的区别,@Transactional使用的注意事项~
1.哪里用@Transactional
- 不要在private 级别的方法上使用@Transactional注解
- 不要在final、static修饰方法上使用@Transactional注解
- 不推荐直接在类上使用@Transactional注解,范围太大没必要
建议在需要事务回滚的Service类中,被Controller或其他Service调用的public且非final和static的方法上添加@Transactional注解 ~
2.Transactional不同参数的区别
@Transactional注解有个关键的参数propagation,它控制着事务的传播行为:
propagation 支持 7 种事务传播特性:
REQUIRED:默认的传播行为,如果当前没有事务,则创建一个新事务;如果存在事务,则加入当前事务。MANDATORY:支持当前事务,如果不存在则抛出异常NEVER:非事务性执行,如果存在事务,则抛出异常REQUIRES_NEW:无论当前是否存在事务,都会创建一个新事务,原有事务被挂起。NESTED:嵌套事务,被调用方法在一个嵌套的事务中运行,这个事务依赖于当前的事务。SUPPORTS:如果当前存在事务,则加入;如果没有,就以非事务方式执行。NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,将其挂起。
REQUIRE:调用方没有事务就新建事务,有就加入调用方事务中
@Service
public class ServiceA {
@Resource
private ServiceC serviceC;
@Transactional
public void testA() {
invokeDataBase();
testA();
serviceC.testC();
}
private void testB() {
invokeDataBase();
}
}
@Service
public class ServiceC {
@Transactional
public void testC() {
invokeDataBase();
}
}
A方法开启事务称之为A事务,B方法未开启事务所以AB方法同一个事务,C方法的事务则加入当前A事务 。
MANDATORY: 调用方没事务抛异常,有事务就加入调用方事务
@Service
public class ServiceA {
@Resource
private ServiceC serviceC;
@Transactional
public void testA() {
invokeDataBase();
serviceC.testC();
}
private void testB() {
invokeDataBase();
serviceC.testC();
}
}
@Service
public class ServiceC {
@Transactional(propagation = Propagation.MANDATORY)
public void testC() {
invokeDataBase();
}
}
A方法有事务,调用C方法:C事务加入A事务
B方法没事务,调用C方法:C方法直接一个异常的抛出!
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
NEVER: 调用方有事务抛异常,调用方无事务自身也没事务
@Service
public class ServiceA {
@Resource
private ServiceC serviceC;
@Transactional
public void testA() {
invokeDataBase();
serviceC.testC();
}
private void testB() {
invokeDataBase();
serviceC.testC();
}
}
@Service
public class ServiceC {
@Transactional(propagation = Propagation.NEVER)
public void testC() {
invokeDataBase();
}
}
B方法没有事务,调用C方法:则B和C都是没事务的普通方法
A方法有事务,调用C方法:则C方法直接一个异常的抛出!
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
REQUIRES_NEW:创建一个新事务,独立于调用方的事务,互不影响
@Service
public class ServiceA {
@Resource
private ServiceC serviceC;
@Transactional
public void testA() {
invokeDataBase();
serviceC.testC();
}
private void testB() {
invokeDataBase();
serviceC.testC();
}
}
@Service
public class ServiceC {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testC() {
invokeDataBase();
}
}
A方法有事务,调用C方法:C事务会成为一个新的事务,A事务不会因为C方法的失败而回滚,也就是A和C互为两个独立的事务。
B方法没有事务,调用C方法:此时REQUIRES_NEW类似于REQUIRE,创建了一个新的事务C
NESTED:调用方有事务则创建一个嵌套子事务,子事务不影响调用方事务和同级事务,但调用方事务会影响子事务
@Service
public class ServiceA {
@Resource
private ServiceC serviceC;
@Transactional
public void testA() {
invokeDataBase();
serviceC.testC();
}
private void testB() {
invokeDataBase();
serviceC.testC();
}
}
@Service
public class ServiceC {
@Transactional(propagation = Propagation.NESTED)
public void testC() {
invokeDataBase();
}
}
A方法有事务,调用C方法:C方法会生成一个嵌套子事务,简而言之外部事务会影响子事务,但子事务的回滚不会影响外部事务,也不会影响同级的其它事务
B方法没有事务,调用C方法:此时NESTED类似于REQUIRE,创建了一个新的事务C
NOT_SUPPORTED: 此方法相当于未开启事务,且此方法的异常不会导致调用方事务回滚
@Service
public class ServiceA {
@Resource
private ServiceC serviceC;
@Transactional
public void testA() {
invokeDataBase();
serviceC.testC();
}
private void testB() {
invokeDataBase();
serviceC.testC();
}
}
@Service
public class ServiceC {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testC() {
invokeDataBase();
}
}
A方法有事务,调用C方法:C方法将以非事务的形式运行,也就是说C方法的异常不会回滚C方法,同时也不会使A事务回滚
B方法没有事务,调用C方法:相当于完全没有启用事务
SUPPORTS:调用方有事务则加入调用方事务中,调用方无事务则不启用事务
@Service
public class ServiceA {
@Resource
private ServiceC serviceC;
@Transactional
public void testA() {
invokeDataBase();
serviceC.testC();
}
private void testB() {
invokeDataBase();
serviceC.testC();
}
}
@Service
public class ServiceC {
@Transactional(propagation = Propagation.SUPPORTS)
public void testC() {
invokeDataBase();
}
}
A方法有事务,调用C方法:C事务将加入调用方的A事务中
B方法没有事务,调用C方法:C方法将不会启用事务
3.@Transactional使用注意事项
- @Transactional注解默认捕获RuntimeException和其子类以及Error类型异常
- try-catch语句中若catch了Exception又不抛出,则事务无法捕获异常并回滚
- 事务具有传播性,即使调用方(启用事务)try-catch了全部异常,但被调用方法(启用事务)抛出异常,事务依旧可以传播给调用方进行回滚
- 检查类异常抛出,事务是不会捕捉到的,要想捕捉需要启用 rollbackFor 参数
- 需要数据库引擎支持事务:例如Mysql MyISAM不支持,InnoDB支持
- 同类方法调用是不会经过代理的,因为这种调用直接通过
this对象进行,不经过 AOP 的切面
public void test() {
invokeDataBase();
a();
b();
}
public void a() {
invokeDataBase();
}
@Transactional
public void b() {
invokeDataBase();
}
- @Transactional 注解通过 AOP 来管理事务,而 AOP 依赖于代理机制。因此,Bean 必须由Spring管理实例
- 在多线程环境下,Spring 的事务管理器不会跨线程传播事务,事务的状态(如事务是否已开启)是存储在线程本地的
ThreadLocal来存储和管理事务上下文信息。这意味着每个线程都有一个独立的事务上下文,事务信息在不同线程之间不会共享
251

被折叠的 条评论
为什么被折叠?



