org.hibernate.PessimisticLockException异常成因与锁类型问询
代码流程
//Service Class A @Transactional mainMethod() { // some business logic methodA() .. } // Component class helperA @Transactional(propagation = Propagation.REQUIRES_NEW) methodA() { // some business logic addLogMethod() } // Component class helperLog addLogMethod(){ // create the log entity repo.save(entity) }
问题描述
执行addLogMethod时触发org.hibernate.PessimisticLockException: could not execute statement异常,抛出点为repo.save(entity)。根因指向外事务施加的悲观锁,而外事务因内部事务启动已被挂起。
现状分析
待保存的日志实体不属于外事务,但该实体与外事务关联的表存在外键关系,这种关联似乎阻止新事务获取对应表的控制权。已验证内部事务可读取外事务锁定的表数据,但不解为何保存日志实体时触发异常——若为Pessimistic.WRITE锁,读取操作应被禁止。
疑问
已知合并事务可解决问题,但想明确:
- 外事务获取的是什么类型的锁?
- 为何会阻止关联外键的日志表新增记录?
解答
1. 外事务的锁类型
外事务持有的是**Pessimistic.WRITE(悲观写锁)**,你能读取锁定数据是因为主流数据库的悲观写锁遵循「共享读、排他写」规则:持有写锁的事务允许其他事务读取数据,仅禁止其他事务修改数据或加写锁,这和你验证的读取正常的现象完全匹配。
2. 阻止日志表新增的原因
问题核心在于外键约束的检查机制:
当内部事务插入日志实体时,数据库必须验证日志实体关联的外键记录是否存在(外键约束的强制要求)。此时外事务已经对关联的主表记录加了Pessimistic.WRITE锁,而外事务因内部事务启动被挂起,锁无法及时释放。内部事务的外键检查操作需要访问被锁定的主表记录,却无法获取所需的锁资源,最终触发锁超时或直接抛出PessimisticLockException。
简单来说:不是日志表被锁,而是插入日志时要验证的主表记录被外事务锁定,外事务挂起导致锁无法释放,内部事务的验证操作被阻塞,进而报错。
内容的提问来源于stack exchange,提问作者vipul jadhav

