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

Hibernate并发访问管理疑问:为何线程可获取已锁定数据库记录?

问题分析与解决方案

核心问题:为何select ... for update没锁住记录?

你遇到的问题本质是事务或锁机制未按预期生效,以下是几个最可能的原因及排查方向:

1. REQUIRES_NEW事务传播属性未生效

Spring的事务注解基于动态代理实现,如果getRecordCondition方法是被同一个类内部的其他方法调用,而非外部通过代理类调用,@Transactional(propagation = Propagation.REQUIRES_NEW)会完全失效。这种情况下,两个线程可能共用同一个事务(或根本没开启新事务),导致select ... for update的锁提前释放。

排查方式:

  • 在getRecordCondition方法的开头和结尾打印日志,输出TransactionSynchronizationManager.getCurrentTransactionName()和TransactionSynchronizationManager.isActualTransactionActive(),确认每次调用都开启了独立的新事务。
  • 确保调用getRecordCondition的代码是通过Spring代理对象调用,而非类内部直接调用。

2. Session管理异常

你提到DAO用getCurrentSession()获取Session,但需确认Session的绑定规则:

  • Spring整合Hibernate时,默认getCurrentSession()返回的Session与当前线程的事务绑定(通过ThreadLocal实现),正常情况下不同线程会有独立的Session。但如果hibernate.current_session_context_class配置错误(比如未设置为org.springframework.orm.hibernate5.SpringSessionContext),可能导致Session未正确绑定到线程,甚至出现多线程共用Session的情况。
  • 检查findRecord方法是否手动关闭了Session:如果在try块里加了session.close(),会直接终止当前事务,导致select ... for update的锁被释放,线程2就能立即获取记录。Spring管理的Session不需要手动关闭,事务结束后会自动回收。

排查方式:

  • 在findRecord和updateRecordStatus方法中打印Session的hashCode(),确认同一个事务内的两个DAO方法使用的是同一个Session。
  • 检查Spring配置文件中Hibernate的current_session_context_class配置项,确保值为org.springframework.orm.hibernate5.SpringSessionContext。

3. select ... for update的锁定时机问题

LockMode.UPGRADE对应的select ... for update锁,只有在当前事务提交或回滚后才会释放。如果findRecord和updateRecordStatus不在同一个事务内,findRecord的事务结束后锁就会释放,线程2就能立即查询到记录。

比如:如果updateRecordStatus方法自己加了@Transactional注解,且传播属性不是REQUIRED,就会开启新事务,导致两个DAO方法分属不同事务,锁提前释放。

Connection与Session的区别

  • Connection:JDBC层面的物理连接,是应用与数据库之间的通信通道,直接用于执行SQL语句、提交/回滚事务。一个Connection同一时间只能被一个线程使用。
  • Session:Hibernate(ORM框架)的会话对象,封装了Connection,提供对象缓存、持久化操作(如save/update)的接口。一个Session通常对应一个Connection,在事务期间会持有Connection,直到事务结束。简单说,Session是ORM对JDBC Connection的封装,让开发者可以用对象操作代替直接写SQL。

多线程是否会共用同一个Session?

正常情况下不会:
Spring整合Hibernate时,Session通过ThreadLocal绑定到当前线程的事务,每个线程的独立事务对应一个独立的Session。只有当配置错误(比如Session被错误注册为单例Bean)、或者手动创建Session并在多线程间共享时,才会出现多线程共用Session的情况,这会导致严重的线程安全问题(比如Connection被多线程同时使用)。

修复建议

  1. 确保@Transactional(propagation = Propagation.REQUIRES_NEW)生效:通过代理对象调用getRecordCondition方法,避免内部调用。
  2. 检查Session配置:确认hibernate.current_session_context_class设置正确,且DAO方法中没有手动关闭Session。
  3. 确保findRecord和updateRecordStatus在同一个事务内:去掉DAO方法上的事务注解(如果有的话),让它们复用服务层的事务。
  4. 验证数据库隔离级别:确保数据库的隔离级别不是READ_UNCOMMITTED(默认是READ_COMMITTED,足够支持select ... for update锁)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 12:24:24