Spring事务失效常见场景

文章详细介绍了Spring框架中事务管理的一些常见问题,包括多线程环境下事务的独立性,只回滚运行时异常的默认行为,指定异常回滚,捕获异常后不会回滚,传播机制的误用,静态方法无法使用事务,本类方法调用导致事务失效,final方法及非public方法的事务限制,以及事务未开启或数据库不支持的情况。
摘要由CSDN通过智能技术生成

1. 多线程调用

两个方法在不同线程中,获取到的数据库连接不一样,从而是两个不同的事务。

public class TestService {
 
    @Autowired
    private Test2Service test2Service;
 
    @Transactional
    public void test(){
 
        new Thread(()->{
            test2Service.test();
        }).start();
    } 
}
 
@Service
public class Test2Service{
 
    @Transactional
    public void test(){
        // do do something
    }
}

2. 非运行时异常

在Spring事务中,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),其他异常不会回滚。

@Service
public class TestService {

    @Transactional
    public void test(){
         // do do something
         throw new Exception("")
    } 
}

3. 指定了异常

如果指定了异常,则其他异常不会回滚

@Service
public class TestService {

    @Transactional(rollbackFor = XXXXException.class)
    public void test(){
         // do do something
         throw new Exception("")
    } 
}

4. 捕获了异常

因为没有抛出异常,所以不会回滚

@Service
public class TestService {

    @Transactional
    public void test(){
        try {
             // do do something
        }catch (Exception e) { 
            e.printStackTrace(); 
        }
    } 
}

5. 错误使用传播机制

我们在使用@Transactional注解时,是可以指定propagation参数的。
该参数的作用是指定事务的传播特性,spring目前支持7种传播特性:
REQUIRED 如果上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。
SUPPORTS 如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
MANDATORY 如果当前上下文中存在事务,否则抛出异常。
REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

如下,test2Service的事务不受TestService管控

public class TestService {
 
    @Autowired
    private Test2Service test2Service;
 
    @Transactional
    public void test(){
        test2Service.test();
    } 
}
 
@Service
public class Test2Service{
 
    @Transactional(propagation = Propagation.NEVER)
    public void test(){
        // do do something
    }
}

6. 类未被Spring管理

没有将该类交给spring管理

//@Service
public class TestService {

    @Transactional
    public void test(){
        try {
             // do do something
        }catch (Exception e) { 
            e.printStackTrace(); 
        }
    } 
}

7. static修饰的方法

@Transactional的原理是spring使用CGLIB或者JDK动态代理的字节码增强技术对原始类方法进行了增强,在次过程过程中会生成一个新的类对象,但static方法是类的,所以无法对staic的方法增强,所以无法使用@Transactional,请看下边例子

public class Father {
    public static void  eat(){
        System.out.println("爸爸吃");
    }
}
public class Son extends Father{
    public static void  eat(){
        System.out.println("儿子吃");
    }

    public static void main(String[] args) {
        //类型是Father 所以输出是爸爸吃,这就类似spring生成的对象 。
        //类型是原本的对象Father ,代理生成对象是Son
        Father f= new Son();
        f.eat();
        //类型是Son 所以输出是儿子吃
        Son s= new Son();
        s.eat();
    }
}

8. 调用本类的其他方法

执行test()方法时test2()的事务不会生效,因为spring会生成两个TestService对象,一个是原本TestService,一个是代理增强后的TestServiceProxy(为了方便取个名),结构类似下面代码。

public class TestService{
   public void test(){
       test2();
    } 
     @Transactional
    public void test2(){
             // do do something
    }   
}

public class TestServiceProxy{
   TestService testService;

   public void test(){
		//如果test有事务,事务开始
		testService.test();
		//如果test有事务事务结束
   }   
    public void test2(){
		//如果test有事务,事务开始
		testService.test2();
		//如果test有事务事务结束
   } 
}

执行过程:TestServiceProxy.test() --> TestService.test() --> TestService.test2()
因为最终执行的是 TestService.test2() ,所以事务无效。

9. final修饰的方法

因为final方法无法重写,所以CGLIB和JDK动态代理无法重写该方法对其增加,所以无法使用@Transactional。

10. 非public修饰的方法

如果方法不是public,Spring事务也会失败,因为Spring的事务管理源码AbstractFallbackTransactionAttributeSource中有判断computeTransactionAttribute()。如果目标方法不是公共的,则TransactionAttribute返回null。所以无法使用@Transactional

// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
  return null;
}

11. 其他

代码没有开启事务、数据库不支持事务。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值