Java事务踩坑

一.基础

1.1 本地写 + rpc写

本地写事务和rpc写操作(最好)不要放在一个事物中。(除非有重试机制)

因为若本地写事务成功了,rpc写操作ReadTimeout执行失败(但是实际上,改rpc操作已经在远程成功了),会导致,本地写操作回滚。造成,本地未写,远程写成功了。

  • 场景1:

1.本地写

2.rpc写

若rpc写失败了,本地成功了。就会导致,不一致。

虽然rpc失败,RuntimeException也抛出来了,并且在最外层也catch住了。这样前端会有错误信息提示。如果用户手动再点一次,就能达到重试的效果,这样,如果本地写幂等了,那么rpc写还有一次重新执行的机会。但是如果用户一看报错了,很蒙圈,直接溜了,不操作了。那么此时就会出现本地数据和远端数据不一致的情况。

  • 场景2:

1、本地写

2、rpc写

如果rpc失败了,用户一看报错就溜了。此时数据不一致了。但是用户下次操作,仍然会从这个报错的地方开始。无形中形成了重试机制,所以能有再次执行rpc的机会。这种场景,本地写和rpc写可以放在一次,允许短暂的数据不一致情况(或者数据不一致和这个人绑定,对其他人的其他数据,没有影响)

  • 场景3:

两个写操作(相同的db,不同的表。但都是本地写),直接事务包一起即可

1.2 transaction事务注解

  • 本地的两个表的写操作一定要加事务注解

  • rpc 写和 本地的写操作,可以使用最大努力重试异步,保证成功。或者是 job触发的,可以在保证幂等的情况下,执行3次job。同样能达到重试的机制

  • 本地的写操作 &&一些列rpc操作:事务最大努力重试保证成功

@Transaction{

     本地写
     @最大努力重试
     rpc

}

二、@Transactional使用注意事项

2.1 踩坑-Methods should not call same-class methods with incompatible “@Transactional” values问题

方法和调用事务的方法,在同一个类中

  • 问题描述

    @Transactional 同一个类中无事务方法a()内部调用有事务方法b()的问题

  • 解决

    ((RdcRefundOutboundBillStatusChangeJob) AopContext.currentProxy()).processBill(bill, param.getDestroyDayLimit())
    
    @Transactional(rollbackFor = Exception.class)
    public boolean processBill(RefundOutboundBillPO bill, int destroyDayLimit) {
      
    }
    
    @Component
    public class AsyncService implements ApplicationContextAware {
    
        private ApplicationContext applicationContext;
    
        public void async1() {//类的某个方法a
            // 使用AppicationContext来获得动态代理的bean,然后再执行你调用的方法
            this.applicationContext.getBean(AsyncService.class).async2();
        }
    
        @Transactional
        public void async2() {
           //本地事务1
          //本地事务2
        }
    
        // 注入ApplicationContext
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

2.1、事务使用注意事项

https://www.cnblogs.com/keeya/p/11180612.html

2.2、事务失效场景

https://segmentfault.com/a/1190000040129676

https://zhuanlan.zhihu.com/p/339536376

类上时,该类所有的public方法都会开启事务;放在方法上时,表示当前方法支持事务,必须在public方法上才可以
需要加在实现类上,而不是方法上
在@Transactional注解中有一个属性rollbackFor,这个属性设置的是发生什么类型的异常时事务进行回滚,默认的是RuntimeException。

2.3、踩坑-多数据源,某一数据源未配置事务name,导致事务失效

  • 问题描述

    NoUniqueBeanDefinitionException: No qualifying bean of type ‘org.springframework.transaction.PlatformTransactionManager’ available: expected single matching bean but found 2: default

  • 原因

    原因

    多数据源的时候

    1、第一个数据源,事务默认名称为default,第二个数据源事务名称为sale

    2、当第一个数据源,使用事务,但是,没有指定事务处理器,就会报这个错误。

    3、因为每个数据源都有自己的事务配置,单纯地用@Transactional 没法确定是哪个事务处理

    解决:
    指定要使用的具体的事务管理器

public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

在@Transactional 注解上,通过value属性或transactionManager = “指定具体的事务管理器名称”

<think>嗯,用户这次明确询问Java事务失效的条件,结合之前的对话历史,ta应该是在开发中遇到了事务未按预期回滚的问题。从引用的资料来看,用户已经查阅过Spring事务失效的相关技术文档,但需要系统化的总结。 用户可能是Java后端开发人员,正在调试事务相关bug。ta的提问方式很专业,直接引用站内资料编号,说明对技术细节有较高要求。不过有趣的是,虽然用户引用了5篇资料,但实际只有前4篇真正讨论事务失效,第5篇更像是配置示例。 重点需要从三个维度整理失效场景: 首先是作用域问题,包括非public方法、内部调用导致代理失效的情况。这里要特别注意用户引用[2]和[4]提到的陷阱——同类内非事务方法调用@Transactional方法会绕过代理,这点容易。 然后是传播机制,用户引用[5]提到的REQUIRES_NEW配置说明不同的传播行为会影响事务边界。需要解释PROPAGATION_SUPPORTS等特殊值在无事务环境下的静默失效。 最后是异常处理,用户引用[3]的案例明显是因为吞掉了编译期异常。值得强调的是Spring默认只回滚RuntimeException这个反直觉设定,以及@Transactional注解rollbackFor属性的必要性。 在整理时会注意三点:一是给出具体代码示例说明问题,比如内部调用用this指针的例子;二是标注每个场景的解决方案,像引用[5]展示的添加事务配置的方法;三是关联用户提供的参考资料,每个失效点对应到具体引用源。 用户可能还需要知道,某些失效场景是设计使然而非bug。比如自调用失效本质是AOP代理机制限制,这正好可以放进回答的防护性编程建议里。 </think>根据您的引用资料,Java事务失效的核心条件可分为以下技术维度,结合Spring框架特性分析: --- ### 一、代理机制失效场景 1. **非public方法** Spring事务基于AOP动态代理实现,仅对`public`方法生效[^2]。 ```java @Transactional // 失效 private void update() { ... } ``` 2. **类内自调用** 同类中非事务方法调用事务方法时,直接调用绕过代理对象[^3][^4]: ```java public void add() { this.update(); // 直接调用导致事务失效 } @Transactional public void update() { ... } ``` --- ### 二、传播行为配置错误 1. **传播机制冲突** 使用`PROPAGATION_NOT_SUPPORTED`等非事务传播行为: ```java @Transactional(propagation = Propagation.NOT_SUPPORTED) // 明确不开启事务 ``` 2. **嵌套事务未隔离** 嵌套事务中若父方法捕获子方法异常但未抛出,导致子事务无法回滚[^5]: ```java @Transactional public void parentMethod() { try { childService.childMethod(); // 子方法抛出异常 } catch (Exception e) { // 捕获异常未抛出 → 父事务提交 } } ``` --- ### 三、异常处理不当 1. **默认回滚异常限制** Spring默认仅对`RuntimeException`和`Error`回滚,其他异常需手动配置[^1][^3]: ```java @Transactional(rollbackFor = Exception.class) // 需显式声明 public void method() throws IOException { ... } ``` 2. **异常被捕获吞没** 事务方法内捕获异常后未重新抛出: ```java @Transactional public void update() { try { ... } catch (Exception e) { log.error(e); // 捕获后未抛出 → 事务提交 } } ``` --- ### 四、技术框架冲突 | 冲突源 | 失效原因 | 解决方案 | |-----------------|-----------------------------------|----------------------------| | **多数据源** | 未指定事务管理器 | `@Transactional("manager1")` | | **异步调用** | `@Async`开启新线程脱离事务上下文 | 拆分事务与异步逻辑 | | **特殊ORM操作** | JPA的`refresh()`/`detach()` | 避免在事务中分离实体 | --- ### 五、防护性编程建议 1. **事务方法隔离原则** -事务方法单独抽离到其他Service类 - 通过`AopContext.currentProxy()`获取代理对象调用: ```java ((UserService) AopContext.currentProxy()).update(); ``` 2. **强制编译检查** 使用自定义注解组合`@Transactional`,避免配置遗漏: ```java @Retention(RetentionPolicy.RUNTIME) @Transactional(rollbackFor = Exception.class, propagation=REQUIRED) public @interface BizTransaction {} ``` > 关键结论:80%的事务失效源于**代理机制绕过**和**异常处理缺失**。建议通过`spring-tx`的`TransactionSynchronizationManager.isActualTransactionActive()`在运行时验证事务状态[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值