Spring事务失效的总结

15 篇文章 0 订阅
9 篇文章 0 订阅

事务失效

1. final/static修饰方法

Spring事务使用AOP,通过CGLIB或JDK动态代理生成代理类,使用final/static修饰方法会导致无法动态代理,继而导致事务失效。

2. 使用非public修饰符修饰

事务方法定义了错误的访问权限(非public修饰)就会使事务失效。

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
    }
    // 省略其他代码
	......
}

上面为AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法代码片段,非public修饰符会直接return null,说明不支持事务。

3. 类忘了加Spring提供的注解导致没有被Spring所管理

例如忘记加@Controller、@Service、@Component、@Repository等注解

4. 使用新的线程执行代码逻辑

public class Test0 {
	@Autowired
    private TestService service;
      
    @Transactional
    public void add(params...) throws Exception {
    	// do something
        new Thread(() -> {
            service.save();
        }).start();
    }
}

@Service
public class TestService {

    @Transactional
    public void save() {
        // do something...
    }
}

事务方法add()调用了另一个事务方法save(),但save()是在另一个线程中调用的,由于获取到的数据库连接不同,是两个不同的事务,因此会失效。

5. 同一个类方法内部调用同类的另一个方法

同一个类方法内部调用同类的另一个方法会使事务失效。

如何解决?

  • 可以用@Autowire注入自己使事务生效
    @Service
    public class Service1 {
       @Autowired
       prvate Service1 service;
    
       public void save(User user) {
       		service.add(user);
       }
    
       @Transactional
       public void add(User user) {
          	 ......
        }
     }
    
  • 新加一个Service类
  • 使用AopContent类
    注:需要在启动类上加注解
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)

来暴露AOP的Proxy对象,否则会报错

Cannot find current proxy: Set ‘exposeProxy’ property on Advised to ‘true’ to make it available.

参考的文章链接

@Service
public class Service1 {

   public void save(User user) {
	   ......
	   ((Service1)AopContext.currentProxy()).add(user);
   }

   @Transactional
   public void add(User user) {
       	......
    }
 }

事务不回滚

spring七种事务传播行为

事物特性说明具体
REQUIRED若当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值spring默认的事务传播行为,A方法调用B方法,如果A方法有事务,则B方法加入到A方法中的事务中,否则B方法自己开启一个新事务
SUPPORTS若当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行A方法调用B方法,如果A方法有事务,则B方法加入到A方法中的事务中,否则B方法自己使用非事务方式执行
MANDATORY若当前上下文中存在事务,否则抛出异常只能在存在事务的方法中被调用,A方法调用B方法,如果A方法没事务,则B方法会抛出异常
REQUIRES_NEW每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行A方法调用B方法,如果A方法有事务,则B方法把A方法的事务挂起,B方法自己重新开启一个新事务
NOT_SUPPORTED若当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行A方法调用B方法,如果A方法有事务,则B方法挂起A方法中的事务中,否则B方法自己使用非事务方式执行
NEVER若当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码不支持事务,A方法调用B方法,如果A方法有事务,则B方法会抛出异常
NESTED若当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务同 Propagation.REQUIRED,不过此传播属性还可以:保存状态节点,从而避免所有嵌套事务都回滚
@Transactional(propagation = Propagation.SUPPORTS)

目前只有这三种传播特性才会创建新事务:REQUIRED,REQUIRES_NEW,NESTED。

参考文章链接

1. 自己手动try catch导致事务不回滚

@Service
public class Service1 {
    @Transactional
    public void save() {
        try {
            add();
        } catch (Exception e) {
            // 打印log
        }
    }
}

2. 手动抛非RuntimeException异常

throw new Exception(e)

Spring事务对于普通的Exception不会回滚,一般只有Error和RuntimeException会回滚。

3. 自定义回滚异常

 @Transactional(rollbackFor = ServiceException.class)
 public void add() throws Exception {
         save();
 }

在执行save()方法时抛出了SqlException、DuplicateKeyException等导致事务不回滚。
最好还是将参数设置为ExceptionThrowable

4. 嵌套回滚了不想回滚的内容

这种内部嵌套事务预期回滚delete(),但是当delete()失败时,insert()也会回滚

public class Service1 {

	@Autowired
    private XxxDao xxxdao;
    @Autowired
    private XxxService xxxService;

    @Transactional
    public void add() throws Exception {
        xxxDao.insert();
        xxxService.delete();
    }
}

@Service
public class XxxService {
    @Transactional(propagation = Propagation.NESTED)
    public void delete() {
        ......
    }
}

可以手动在add中使用try…catch包裹xxxService.delete();

@Transactional
    public void add(UserModel userModel) throws Exception {
		xxxDao.insert();
        try {
            xxxService.delete();
        } catch (Exception e) {
            // 打印log日志
        }
    }

总结:上面是一些常见的事务问题,除此之外有些事务方法select比较多,(实际需要事务的可能只有一两行insert,update),调用层级很深且查询耗时高,会导致整个事务相当耗时导致大事务问题。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值