You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate标注@Transactional时数据表行锁的触发时机是什么?

Hibernate @Transactional注解下的行锁触发时机解答

明确结论

默认配置下,你给出的示例代码中,行锁既不会在进入test方法时触发,也不会在首次查询时触发,而是在事务提交前Hibernate flush变更、执行对应写SQL的阶段触发,对应到代码执行时序,本质和save操作绑定,但不是调用save方法的瞬间加锁,是写SQL实际发往数据库执行时由数据库加行锁。

原理说明

我们结合你给出的两个场景逐一解释:

1. 无@Transactional的场景

你提到未加@Transactional时调用save()就触发行锁,本质是Spring Data JPA默认会为每个Repository方法开启独立的短事务:

  • 调用a.save(b)时,Spring立即开启短事务,发送UPDATE/INSERT SQL到数据库,数据库执行SQL时加行锁
  • 方法执行结束事务自动提交,行锁释放,所以直观感受是调用save()就触发了锁

2. 加@Transactional的场景

对应你给出的代码:

@Transactional
public void test() {
    B b = a.findById(1);
    B b2 = a.findById(2);
    a.save(b);
    a.save(b2);
}

默认配置下的执行和加锁时序如下:

  • 进入test方法:Spring仅完成事务上下文初始化,绑定数据库连接到当前线程,未发送任何SQL到数据库,无锁
  • 执行findById(1)、findById(2):默认发送普通快照读SELECT语句,MySQL InnoDB引擎在REPEATABLE READ默认隔离级别下,快照读不会加任何行锁,无锁
  • 调用a.save(b)、a.save(b2):仅把实体变更记录到Hibernate持久化上下文的待刷出队列,不会立即发送写SQL到数据库,无锁
  • 方法执行结束触发事务提交:Hibernate先执行flush操作,按顺序发送两条UPDATE SQL到数据库,数据库执行第一条UPDATE时给id=1的行加排他行锁,执行第二条UPDATE时给id=2的行加排他行锁,事务提交完成后两把锁自动释放

特殊例外场景

只有出现以下配置时,加锁时机才会提前:

  • 若查询方法标注了@Lock(LockModeType.PESSIMISTIC_WRITE)等显式悲观锁注解,行锁会在对应findById查询执行时触发
  • 若数据库隔离级别设置为SERIALIZABLE,普通SELECT查询也会加共享锁,首次查询时就会触发行锁
  • 若显式调用entityManager.flush()或者修改了Hibernate的flush策略为提前刷出,写SQL会更早发送,锁的触发时机也会对应提前,但本质还是执行写SQL时加锁

内容的提问来源于stack exchange,提问作者SinLok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 17:45:03