昨天一个应用了很久的功能突然报错500,追踪日志后发现是Spring的事务管理报了错
Transaction rolled back because it has been marked as rollback-only
报错信息是:”Transaction rolled back because it has been marked as rollback-only“
字面意思翻译就是:事务回滚了,因为它被标记为必须回滚
要想理解这句话就必须先知道 Spring 事务管理的一种传播属性: 嵌套事务
顾名思义 嵌套事务 就是 A B 两个事务,B 事务作为一个 A 的内部事务,如果 B事务失败则会给外层事务也就是 A 事务一个标记,标记 A 事务也必须回滚,否则会导致数据不一致
知道了什么是嵌套事务后我们再来理解这句话,意思就是 内层事务报了错,影响到了外层事务,所以只需要进入内部方法查看报错信息即可,由于代码比较久远质量比较差,代码没有打印错误信息,尝试自己调用却无法复现事务报错,这是为什么?
是因为 Spring 事务是基于代理的方式去做,自己手动调方法自然是行不通的。
继续深入内层,最后将报错原因锁定在了数据库外键约束处理上
报了如下错误:
Cannot add or update a child row: a foreign key constraint fails (`client_group`, CONSTRAINT `fk_client_id` FOREIGN KEY (`client_id`) REFERENCES `client_old` (`client_id`)
是一个外键约束报错,表结构如下:
----- group 群组表
----- client 用户表
----- client_group 关联表
关联表设置了两个外键,分别是 client_id 和 group_id,如果两个外键没有从对应的 client 、group表找到合适的 id 范围,就会报错
但是由于前不久其他的开发更新了数据库,将原先的 client 表拷贝了一个新表,旧表就改为了没有数据的 client_old 表,但是由于外键没有更新,存储的仍然是 client_old 表的 id,所以引发了数据报错,这种情况就需要我们去更新一下外键,让它和 新表建立联系即可。
更新外键操作如下:
首先需要 禁止外键约束,否则无法新增更改外键
SET FOREIGN_KEY_CHECKS=0
再删除之前的外键
alter table client_group drop foreign key fk_client_id;
在插入一个外键
alter table results add constraint fk_client_id foreign key(client_id) references client(client_id);
最后不要忘记将外键约束打开哦!
SET FOREIGN_KEY_CHECKS=1
至此 bug 修复完毕,系统可以正常的运行,经过这一次修复,我的知识库得以扩充,很多一起书本上生冷的知识瞬间变得活化火热进入我的大脑;也提醒了我们线上数据库的修改一定要谨小慎微,任何一个改动都可能导致之前积压的设计崩塌。