Spring:@Transactional一次排查问题记录

一.服务异常描述
微服务架构,根据日志定位到错误是在B服务的某个方法插入表数据时抛出的,错误代码是:

ORA-02291: 违反完整约束条件 (*) - 未找到父项关键字

伪代码如下:

@Transactional
public void serviceB(){
  int update = xxxMapper.save(beanB);
}

数据库用的是Oracle,错误提示也很明显.
ORA-02291: 违反完整约束条件 (*) - 未找到父项关键字总体说说可能出现的原因:情况场景:表A中有个字段是外键,关联了表B中的某字段,再往表A插入数据时,会出现这种情况。可能原因:1.在往A表插入时,外键关联的字段在B表中必须有数据,如果B表中没有数据则又这种情况。 2.产生了外键环,就是B表中被外键关联的字段又关联了C表中的字段,而C中相应字段却没有数据,则产生这种情况。3.如果不是上两种情况,那么就是一个非常容易疏忽的问题:A中的外键字段和B中的被外键关联字段数据类型和长度不一致。特别是数据长度,必须要一致。第三种情况是最容易忽略的,希望大家注意。
然后通过PL/SQL查看benaB对应的表假设为table_b中的外键约束,发现错误提示的外键约束表假设为table_a,table_b中的外键假设为FK_tableA_tableB,约束关系为table_b(id_tableA)->table_a(id),而id_tableA对应的字段值是另一个服务A远程调用传过来的,假设A中的伪代码如下:

@Transactional(readOnly = false, rollbackFor = { Exception.class })
public void submitData(){
  int update = xxxMapper.save(beanA);
  HttpClient.post(B.serviceB(paramJson));
  ...
}

在A服务中,先进行beanA数据保存(beanA对应的表为table_a),保存成功id会自动会写到beanA中,然后在 HttpClient.post(B.serviceB(paramJson))会传递到过去.

二.问题排查
table_a和table_b位于同一个数据库下,schema相同,且在服务A和服务B中连接串相同.
A服务中明明保存成功了,值也传到位了,为什么还说找不到呢?
看到了@Transactional注解,想到了可能和隔离级别有关.
假设隔离级别为READ_COMMITTED,A服务中的保存成功了,但此时事务还未提交,然后到了B服务中在保存时会因为强外键约束,读取不到A中还未提交的数据报了错误.既然这样,我代码中设置下A服务submitData()方法的隔离级别:

@Transactional(readOnly = false, rollbackFor = { Exception.class },isolation = Isolation.READ_UNCOMMITTED)
public void submitData(){
  int update = xxxMapper.save(beanA);
  HttpClient.post(B.serviceB(paramJson));
  ...
}

我把隔离级别改成读未提交的,结果测试报出如下错误:这里写图片描述
百度了下,原来oracle只支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别,且默认系统事务隔离级别是READ COMMITTED,也就是读已提交.其实想想也知道,这中即使支持也是不起作用的,毕竟是跨jvm了,涉及到分布式事务了.

三.解决办法
1.去掉A中方法的事务,同时为了防止A服务保存数据成功了,但B服务调用失败,产生脏数据,进行代码编程手动删除替代自动回滚操作.
2.去掉外键约束
另注:在这中场景下,传播机制是不起作用的.
考虑是否要上个分布式事务呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果使用了@Transactional注解但是没有回滚的话,可以按照以下步骤进行排查: 1. 确保@Transactional注解应用在public可见度的方法上。因为它只能应用于public方法,如果应用在protected、private或者package可见度的方法上,事务设置不会起作用。 2. 检查方法是否被正确地调用。@Transactional注解只对外部调用的方法起作用,如果方法是通过内部调用或者通过this关键字调用的,事务设置可能不会生效。 3. 确保方法被正确地代理了。@Transactional注解是通过Spring AOP来实现的,所以被注解的方法必须通过代理来执行才能触发事务管理器的逻辑。如果方法没有被代理,事务设置也不会生效。 4. 检查事务传播属性是否正确设置。事务传播属性决定了方法在一个现有事务中运行还是创建一个新的事务。如果使用了NESTED属性,内部事务的回滚不会对外部事务造成影响。如果使用了REQUIRED属性,没有活动事务时才会创建一个新的事务。 5. 检查是否配置了只读事务。只读事务的标志只在事务启动时应用,如果事务已经启动则该配置会被忽略。因此,如果只是读取数据而没有修改操作,不必设置只读事务,避免增加额外的系统开销。 通过以上排查步骤,可以定位并解决@Transactional注解没有回滚的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [@Transactional 回滚不生效原因](https://blog.csdn.net/nmjhehe/article/details/82796028)[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_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值