知識庫 / Spring RSS 訂閱

Hibernate 異常:線程綁定空 Hibernate Session 在 Hibernate 3 中

Persistence,Spring
HongKong
4
02:13 PM · Dec 06 ,2025

1. 簡介

本教程將闡明“未綁定線程的 Hibernate 會話”異常何時被拋出以及如何解決它。

我們將重點關注以下兩個場景:

  1. 使用LocalSessionFactoryBean
  2. 使用AnnotationSessionFactoryBean

2. 根本原因在版本 3 中,Hibernate 引入了上下文會話的概念,並且為 getCurrentSession() 方法添加到了 SessionFactory 類中。有關上下文會話的更多信息,請參見 這裏

Spring 自身實現了 org.hibernate.context.CurrentSessionContext 接口——org.springframework.orm.hibernate3.SpringSessionContext (在 Spring Hibernate 3 的情況下)。該實現要求會話與事務綁定。

自然地,調用 getCurrentSession() 方法的類應在類級別或方法級別上使用 @Transactional 註解。如果未這樣做,則會拋出 org.hibernate.HibernateException: No Hibernate Session Bound to Thread 異常。

讓我們來看一個示例。

3. LocalFactorySessionBean

這是我們在本文檔中首先要考慮的場景。

我們將定義一個帶有 LocalSessionFactoryBean 的 Java Spring 配置類:

@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;
    }    
    // ...
}

請注意,我們在此使用 Hibernate 配置文件 (exceptionDemo.cfg.xml) 以進行模型類的映射。這是因為 org.springframework.orm.hibernate3.LocalSessionFactoryBean 不提供 packagesToScan 屬性,用於映射模型類。

以下是我們的簡單服務:

@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;
    
    // ...
 }

如代碼片段所示,getCurrentSession() 方法是用於從 SessionFactory 類中獲取 Hibernate 會話的。

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();
    }
}

以下測試通過,展示了當類 EventService 缺少 @Transactional 註解時,異常將會被拋出的情況:

@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"));
    }
}

此測試展示了當 EventService 類被註解為 @Transactional 註解時,服務方法成功執行的方式:

@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

這個異常也可能在我們使用 org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean 來在我們的 Spring 應用中創建 SessionFactory 時發生。

讓我們來看一些演示此現象的示例代碼。為此,我們定義了一個使用 AnnotationSessionFactoryBean 的 Java Spring 配置類:

@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;
    }
    // ...
}

使用與上一節相同的 DAO、Service 和 Model 類集,我們遇到上面描述的異常:

@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"));
    }
}

如果我們將服務類註解為 @Transactional 註解,則服務方法按預期工作,並且下面的測試通過。

@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. 解決方案

顯然,從 Spring 獲取的 SessionFactory 中的 getCurrentSession() 方法必須在打開的事務中調用。因此,解決方案是確保我們的 DAO/Service 方法/類正確地使用 @Transactional 註解。

需要注意的是,在 Hibernate 4 及更高版本中,由於相同原因拋出的異常消息有所不同。我們不再得到“No Hibernate Session Bound to Thread”,而是得到“Could not obtain transaction-synchronized Session for current thread”

還有另一個重要的要點。 隨着 org.hibernate.context.CurrentSessionContext 接口的引入,Hibernate 還引入了 hibernate.current_session_context_class 屬性,該屬性可以設置為當前會話上下文的實現類。

正如之前所述,Spring 提供了該接口的自身實現:SpringSessionContext。 默認情況下,它會將 hibernate.current_session_context_class 屬性設置為該類。

因此,如果我們顯式地將此屬性設置為其他值,則會干擾 Spring 管理 Hibernate 會話和事務的能力,從而導致另一個異常,但與所考慮的異常不同。

總結一下,當我們使用 Spring 管理 Hibernate 會話時,我們不應顯式地設置 hibernate.current_session_context_class 屬性。

6. 結論

在本文中,我們探討了當異常org.hibernate.HibernateException: No Hibernate Session Bound to Thread在 Hibernate 3 中被拋出時的原因,以及如何通過示例代碼輕鬆解決它。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.