1. 引言
本文将深入探讨 "No Hibernate Session Bound to Thread" 异常的触发场景及解决方案。我们将聚焦两种典型配置场景:
- 使用
LocalSessionFactoryBean
- 使用
AnnotationSessionFactoryBean
2. 异常根源
Hibernate 3 引入了上下文会话(contextual session)概念,并在 SessionFactory
类中新增了 getCurrentSession()
方法。Spring 提供了该接口的实现类 org.springframework.orm.hibernate3.SpringSessionContext
,该实现要求会话必须绑定到事务。
⚠️ 关键点:调用 getCurrentSession()
的类必须添加 @Transactional
注解(类级别或方法级别),否则将抛出异常。
3. LocalFactorySessionBean 场景
3.1 配置示例
@Configuration
@EnableTransactionManagement
@PropertySource(
{ "classpath:persistence-h2.properties" }
)
@ComponentScan(
{ "com.baeldung.persistence.dao", "com.baeldung.persistence.service" }
)
public class PersistenceConfigHibernate3 {
// ...
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory
= new LocalSessionFactoryBean();
Resource config = new ClassPathResource("exceptionDemo.cfg.xml");
sessionFactory.setDataSource(dataSource());
sessionFactory.setConfigLocation(config);
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
// ...
}
💡 注意:
LocalSessionFactoryBean
不支持packagesToScan
属性,需通过 XML 配置文件映射实体类。
3.2 服务层实现
@Service
@Transactional
public class EventService {
@Autowired
private IEventDao dao;
public void create(Event entity) {
dao.create(entity);
}
}
@Entity
@Table(name = "EVENTS")
public class Event implements Serializable {
@Id
@GeneratedValue
private Long id;
private String description;
// ...
}
3.3 DAO 层实现
public abstract class AbstractHibernateDao<T extends Serializable>
implements IOperations<T> {
private Class<T> clazz;
@Autowired
private SessionFactory sessionFactory;
// ...
@Override
public void create(T entity) {
Preconditions.checkNotNull(entity);
getCurrentSession().persist(entity);
}
protected Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
3.4 异常复现测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
classes = { PersistenceConfigHibernate3.class },
loader = AnnotationConfigContextLoader.class
)
public class HibernateExceptionScen1MainIntegrationTest {
@Autowired
EventService service;
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void whenNoTransBoundToSession_thenException() {
expectedEx.expectCause(
IsInstanceOf.<Throwable>instanceOf(HibernateException.class));
expectedEx.expectMessage("No Hibernate Session bound to thread, "
+ "and configuration does not allow creation "
+ "of non-transactional one here");
service.create(new Event("from LocalSessionFactoryBean"));
}
}
3.5 正常场景测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
classes = { PersistenceConfigHibernate3.class },
loader = AnnotationConfigContextLoader.class
)
public class HibernateExceptionScen1MainIntegrationTest {
@Autowired
EventService service;
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void whenEntityIsCreated_thenNoExceptions() {
service.create(new Event("from LocalSessionFactoryBean"));
List<Event> events = service.findAll();
}
}
4. AnnotationSessionFactoryBean 场景
4.1 配置示例
@Configuration
@EnableTransactionManagement
@PropertySource(
{ "classpath:persistence-h2.properties" }
)
@ComponentScan(
{ "com.baeldung.persistence.dao", "com.baeldung.persistence.service" }
)
public class PersistenceConfig {
//...
@Bean
public AnnotationSessionFactoryBean sessionFactory() {
AnnotationSessionFactoryBean sessionFactory
= new AnnotationSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
new String[] { "com.baeldung.persistence.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
// ...
}
4.2 异常复现测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
classes = { PersistenceConfig.class },
loader = AnnotationConfigContextLoader.class
)
public class HibernateExceptionScen2MainIntegrationTest {
@Autowired
EventService service;
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void whenNoTransBoundToSession_thenException() {
expectedEx.expectCause(
IsInstanceOf.<Throwable>instanceOf(HibernateException.class));
expectedEx.expectMessage("No Hibernate Session bound to thread, "
+ "and configuration does not allow creation "
+ "of non-transactional one here");
service.create(new Event("from AnnotationSessionFactoryBean"));
}
}
4.3 正常场景测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
classes = { PersistenceConfig.class },
loader = AnnotationConfigContextLoader.class
)
public class HibernateExceptionScen2MainIntegrationTest {
@Autowired
EventService service;
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void whenEntityIsCreated_thenNoExceptions() {
service.create(new Event("from AnnotationSessionFactoryBean"));
List<Event> events = service.findAll();
}
}
5. 解决方案
✅ 核心解决方案:确保调用 getCurrentSession()
的方法/类添加 @Transactional
注解。
📌 版本差异提示:
- Hibernate 3 异常信息:
No Hibernate Session Bound to Thread
- Hibernate 4+ 异常信息:
Could not obtain transaction-synchronized Session for current thread
⚠️ 避坑指南:切勿手动设置 hibernate.current_session_context_class
属性!Spring 会自动配置 SpringSessionContext
,手动设置会破坏事务管理机制。
6. 总结
本文通过两种典型配置场景,解析了 Hibernate 3 中 No Hibernate Session Bound to Thread
异常的触发原因及解决方案。关键点总结:
- 必须在事务上下文中调用
getCurrentSession()
- 正确使用
@Transactional
注解 - 避免手动配置
hibernate.current_session_context_class
完整代码示例请参考:GitHub 仓库