HibernateException: No Hibernate Session Bound to Thread in Hibernate 3 – HibernateException:在Hibernate 3中没有与线程绑定的Hibernate会话

最后修改: 2017年 11月 4日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this short tutorial, we’ll clarify when “No Hibernate Session Bound to Thread” exception gets thrown and how to resolve it.

在这个简短的教程中,我们将阐明何时抛出 “没有Hibernate会话绑定到线程 “的异常以及如何解决它。

We’ll be focusing here on two different scenarios:

我们将在这里重点讨论两种不同的情况。

  1. using the LocalSessionFactoryBean
  2. using the AnnotationSessionFactoryBean

2. The Cause

2.原因

With version 3, Hibernate introduced the concept of the contextual session and the getCurrentSession() method was added to the SessionFactory class. More information on the contextual session can be found here.

在第3版中,Hibernate引入了上下文会话的概念,getCurrentSession() 方法被添加到SessionFactory类。关于上下文会话的更多信息可以在这里找到。

Spring has its own implementation of the org.hibernate.context.CurrentSessionContext interface – org.springframework.orm.hibernate3.SpringSessionContext (in the case of Spring Hibernate 3). This implementation requires the session to be bound to a transaction.

Spring对org.hibernate.context.CurrentSessionContext接口有自己的实现–org.springframework.orm.hibernate3.SpringSessionContext(在Spring Hibernate 3的情况下)。该实现需要将会话绑定到一个事务上。

Naturally, classes that call getCurrentSession() method should be annotated with @Transactional either at the class level or method level. If not, the org.hibernate.HibernateException: No Hibernate Session Bound to Thread will be thrown.

当然,调用getCurrentSession()方法的类应该被注解为@Transactional,无论是在类级别还是方法级别。如果没有,org.hibernate.HibernateException:No Hibernate Session Bound to Thread 将会被抛出。

Let’s have a quick look at an example.

让我们快速看一下一个例子。

3. LocalFactorySessionBean

3、LocalFactorySessionBean

He’s the first scenario that we would be looking at in this article.

他是我们在这篇文章中要关注的第一种情况。

We will define a Java Spring configuration class with LocalSessionFactoryBean:

我们将用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;
    }    
    // ...
}

Note that we use a Hibernate configuration file (exceptionDemo.cfg.xml) here in order to map the model class. This is because the org.springframework.orm.hibernate3.LocalSessionFactoryBean does not provide the property packagesToScan, for mapping model classes.

请注意,我们在这里使用了一个Hibernate配置文件(exceptionDemo.cfg.xml),以便映射模型类。这是因为org.springframework.orm.hibernate3.LocalSessionFactoryBean不提供packagesToScan属性,用于映射模型类。

Here’s our simple service:

下面是我们的简单服务。

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

As we can see in the code snippet below, the getCurrentSession() method of the SessionFactory class is used to obtain the Hibernate session:

正如我们在下面的代码片段中看到的,SessionFactory 类的getCurrentSession() 方法被用来获取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();
    }
}

The test below passes, demonstrating how the exception will be thrown when the class EventService containing the service method is not annotated with a @Transactional annotation:

下面的测试通过了,证明了当包含服务方法的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"));
    }
}

This test shows how the service method executes successfully when the EventService class is annotated with the @Transactional annotation:

这个测试显示了当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

4、AnnotationSessionFactoryBean

This exception can also occur when we use the org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean to create the SessionFactory in our Spring application.

当我们在Spring应用程序中使用org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean来创建SessionFactory时,也会出现这种异常。

Let’s look at some sample code that demonstrates this. To this extent, we define a Java Spring configuration class with AnnotationSessionFactoryBean:

让我们来看看一些演示的示例代码。在这个范围内,我们用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;
    }
    // ...
}

With the same set of DAO, Service and Model classes from the previous section, we encounter the exception as described above:

用上一节中的同一组DAO、服务和模型类,我们遇到了上述的异常。

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

If we annotate the service class with a @Transactional annotation, the service method works as expected and the test shown below passes:

如果我们用@Transactional annotation来注解服务类,服务方法就会像预期的那样工作,下面的测试就会通过。

@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. The Solution

5.解决方案

It’s clear that the getCurrentSession() method of the SessionFactory obtained from Spring needs to be called from within an open transaction. Therefore, the solution is to ensure that our DAO/Service methods/classes are annotated correctly with the @Transactional annotation.

很明显,从Spring获得的SessionFactorygetCurrentSession()方法需要从一个开放的事务中调用。因此,解决方案是确保我们的DAO/服务方法/类被正确注释为@Transactional注释。

It should be noted that in Hibernate 4 and later versions, the message of the exception that is thrown for this same reason is differently worded. Instead of the “No Hibernate Session Bound to Thread”, we’d get “Could not obtain transaction-synchronized Session for current thread”.

需要注意的是,在Hibernate 4及以后的版本中,因同样原因抛出的异常信息的措辞是不同的。我们得到的不是”没有与线程绑定的 Hibernate 会话”,而是”无法为当前线程获得事务同步的会话”

There’s another important point to make. Along with the org.hibernate.context.CurrentSessionContext interface, Hibernate has introduced a property hibernate.current_session_context_class which can be set to the class that implements the current session context.

还有一个很重要的问题要说明。除了org.hibernate.context.CurrentSessionContext接口,Hibernate还引入了一个属性hibernate.current_session_context_class,它可以被设置为实现当前会话上下文的类。

As stated before, Spring comes with its own implementation of this interface: the SpringSessionContext. By default it sets the hibernate.current_session_context_class property equal to this class.

如前所述,Spring自带了这个接口的自己的实现:SpringSessionContext。默认情况下,它将hibernate.current_session_context_class属性设为该类。

As a consequence, if we explicitly set this property to something else, it disrupts Spring’s ability to manage the Hibernate session and transactions. This results in an exception as well but is different from the exception under consideration.

因此,如果我们明确地将这个属性设置为其他的东西,就会破坏Spring管理Hibernate会话和事务的能力。这也会导致一个异常,但与我们所考虑的异常不同。

Summarizing, it is important to remember that we should not set the hibernate.current_session_context_class explicitly when we use Spring to manage the Hibernate session.

综上所述,重要的是要记住,当我们使用Spring来管理Hibernate会话时,我们不应该明确地设置hibernate.current_session_context_class

6. Conclusion

6.结论

In this article, we looked at why an when the exception org.hibernate.HibernateException: No Hibernate Session Bound to Thread is thrown in Hibernate 3 along with some example code and how we can solve it easily.

在这篇文章中,我们研究了为什么在Hibernate 3中出现异常 org.hibernate.HibernateException:No Hibernate Session Bound to Thread 在Hibernate 3中被抛出,以及一些示例代码和我们如何轻松解决它。

The code for this article can be found over on Github.

这篇文章的代码可以在Github上找到over