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),调用层级很深且查询耗时高,会导致整个事务相当耗时导致大事务问题。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring中,事务可能会失效的几个场景包括: 1. 未配置事务管理器:如果在项目中没有配置Spring事务管理器,即使使用了Spring事务管理功能,事务也不会生效。[2] 2. 数据库不支持事务Spring事务的生效前提是所连接的数据库要支持事务。如果底层的数据库不支持事务,那么Spring事务肯定会失效。举个例子,如果使用的数据库为MySQL,并且选用了MyISAM存储引擎,那么Spring事务就会失效。 3. 事务方法未被Spring管理:另一个导致事务失效的场景是事务方法未被Spring管理。要使Spring事务生效,需要在应用程序中使用@Transactional注解或配置声明式事务的XML配置,来标记需要进行事务管理的方法。如果方法未被正确标记,那么Spring将无法管理该方法的事务,从而导致事务失效总结起来,Spring事务可能失效的场景包括未配置事务管理器、数据库不支持事务以及事务方法未被Spring管理。在使用Spring事务管理功能时,需要注意这些场景,以确保事务的正常生效。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Spring 事务失效的 8 种场景!](https://blog.csdn.net/sufu1065/article/details/122076645)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值