Рубрики
Без рубрики

Исключение HibernateException: Сеанс Hibernate не привязан к потоку в Hibernate 3

Узнайте, когда возникает исключение “Нет сеанса гибернации, привязанного к потоку”, и как с ним справиться.

Автор оригинала: baeldung.

1. введение

В этом коротком уроке мы уточним, когда возникает исключение “Нет сеанса гибернации, привязанного к потоку”, и как его устранить.

Мы сосредоточимся здесь на двух различных сценариях:

  1. использование LocalSessionFactoryBean
  2. использование AnnotationSessionFactoryBean

2. Причина

В версии 3 Hibernate представила концепцию контекстного сеанса, и метод getCurrentSession() был добавлен в класс SessionFactory . Более подробную информацию о контекстной сессии можно найти здесь .

Spring имеет свою собственную реализацию org.hibernate.context.CurrentSessionContext interface – org.springframework.orm.hibernate3.SpringSessionContext (в случае Spring Hibernate 3). Эта реализация требует, чтобы сеанс был привязан к транзакции.

Естественно, классы, вызывающие метод getCurrentSession () , должны быть аннотированы с помощью @Transactional либо на уровне класса, либо на уровне метода. Если нет, то org.hibernate.Исключение HibernateException: Не будет создан сеанс Hibernate, привязанный к потоку .

Давайте быстро рассмотрим пример.

3. Локальный Заводской сеанс Bean

Он – первый сценарий, который мы рассмотрим в этой статье.

Мы определим класс конфигурации Java Spring с помощью LocalSessionFactoryBean :

@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 используется для получения сеанса гибернации:

public abstract class AbstractHibernateDao 
  implements IOperations {
    private Class 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.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"));
    }
}

Этот тест показывает, как метод службы успешно выполняется, когда класс Event Service аннотируется аннотацией @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 events = service.findAll();
    }
}

4. AnnotationSessionFactoryBean

Это исключение также может возникнуть при использовании аннотации org.springframework.orm.hibernate3.AnnotationSessionFactoryBean для создания SessionFactory в нашем весеннем приложении.

Давайте рассмотрим пример кода, который демонстрирует это. В этой степени мы определяем класс конфигурации Java Spring с помощью AnnotationSessionFactoryBean :

@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, служб и моделей из предыдущего раздела мы сталкиваемся с исключением, как описано выше:

@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.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 events = service.findAll();
    }
}

5. Решение

Ясно, что getCurrentSession() метод SessionFactory , полученный из Spring, должен быть вызван из открытой транзакции. Поэтому решение состоит в том, чтобы убедиться, что ваши методы/классы DAO/Service правильно аннотированы аннотацией @Transactional .

Следует отметить, что в Hibernate 4 и более поздних версиях сообщение об исключении, вызванном по той же причине, имеет другую формулировку. Вместо ” Нет сеанса гибернации, привязанного к потоку”, мы получим ” Не удалось получить сеанс синхронизации транзакций для текущего потока”.

Есть еще один важный момент. Наряду с интерфейсом org.hibernate.context.CurrentSessionContext в Hibernate введено свойство hibernate.current_session_context_class , которое может быть установлено в класс, реализующий текущий контекст сеанса.

Как уже говорилось ранее, Spring поставляется со своей собственной реализацией этого интерфейса: SpringSessionContext. По умолчанию он устанавливает свойство hibernate.current_session_context_class равным этому классу.

Как следствие, если мы явно установим это свойство на что-то другое, это нарушит способность Spring управлять сеансом гибернации и транзакциями. Это также приводит к исключению, но отличается от рассматриваемого исключения.

Подводя итог, важно помнить, что мы не должны устанавливать hibernate.current_session_context_class явно, когда мы используем Spring для управления сеансом Hibernate.

6. Заключение

В этой статье мы рассмотрели, почему и когда исключение org.hibernate.Исключение HibernateException: В Hibernate 3 нет сеанса Hibernate, привязанного к потоку , вместе с некоторым примером кода и тем, как мы можем легко его решить.

Код для этой статьи можно найти на Github .