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
相关产品推荐
相关产品推荐

