Spring Data JPA @Lock注解搭配@Transactional的锁定行为疑问
@Lock注解使用问题解答
首先明确核心结论:@Lock注解仅对Spring Data JPA Repository层的查询方法生效,标注在Service层的@Transactional方法上不会触发任何加锁逻辑,你观测到的现象和@Lock在Service层生效无关,官方文档的说明完全准确。
测试现象拆解
- 执行
repo.findById(id)时没有生成select for update语句,已经直接证明Service层的@Lock注解没有生效:Spring Data JPA仅会在处理Repository接口的方法调用时,扫描方法上的@Lock注解并生成对应的加锁SQL,Service层的@Lock注解不会被JPA的切面识别。 - 你观测到的事务T2更新超时是数据库原生行锁机制导致的,和@Lock无关:事务T1持有对应记录的快照读权限,事务T2发起更新时需要获取对应行的写锁,必须等待T1事务提交/回滚才能获取锁,等待超出数据库配置的锁超时时间就会抛出悲观锁超时异常。你可以删除Service层的@Lock注解后重复测试,会得到完全一致的结果。
额外验证点:你可以查看数据库的事务日志和锁等待日志,能看到T2的锁等待是因为更新操作触发,而非@Lock生成的加锁查询触发。
疑问明确答复
@Lock搭配@Transactional使用时,不会给该事务内所有查询到的行都加锁。只有将@Lock直接标注在Repository的查询方法(包括@Query自定义查询、findBy*派生查询)上时,才会给对应单次查询返回的行加锁,且需要外层事务支撑锁的持有周期。
正确的悲观锁配置方式
如果你需要给findById查询加悲观写锁,需要将@Lock注解放到Repository接口的对应方法上:
public interface InventoryRepository extends JpaRepository<Inventory, Integer> { @Lock(LockModeType.PESSIMISTIC_WRITE) Optional<Inventory> findById(Integer id); }
配置完成后调用该方法,就会生成select ... for update的加锁SQL,真正触发JPA层面的悲观锁逻辑。
内容的提问来源于stack exchange,提问作者SBhogal
相关产品推荐
相关产品推荐

