«1. Введение
В этом кратком руководстве мы объясним, когда возникает исключение «Нет Hibernate Session Bound to Thread» и как его разрешить.
Здесь мы сосредоточимся на двух разных сценариях:
- using the LocalSessionFactoryBean
- using the 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
Это первый сценарий, который мы рассмотрим в этой статье.
Мы определим класс конфигурации 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;
// ...
}
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();
}
}
Как видно из фрагмента кода ниже, метод getCurrentSession() класса SessionFactory используется для получения сеанса Hibernate:
@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();
}
}
Этот тест показывает, как метод службы успешно выполняется, когда класс EventService аннотирован с аннотацией @Transactional:
4. AnnotationSessionFactoryBean
Это исключение также может возникнуть, когда мы используем org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean для создания SessionFactory в нашем приложении 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;
}
// ...
}
Давайте посмотрим на пример кода, демонстрирующий это. Для этого мы определяем класс конфигурации Java Spring с помощью AnnotationSessionFactoryBean:
@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"));
}
}
С тем же набором классов 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 whenEntityIsCreated_thenNoExceptions() {
service.create(new Event("from AnnotationSessionFactoryBean"));
List<Event> events = service.findAll();
}
}
Если мы аннотируем класс службы аннотацией @Transactional, метод службы работает должным образом, и тест, показанный ниже, проходит успешно:
5. Решение
Понятно, что метод getCurrentSession() класса SessionFactory получил from 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 и транзакциями. Это также приводит к исключению, но отличается от рассматриваемого исключения.
«Подводя итог, важно помнить, что мы не должны явно устанавливать hibernate.current_session_context_class, когда используем Spring для управления сеансом Hibernate.
6. Заключение
В этой статье мы рассмотрели, почему исключение org.hibernate.HibernateException: No Hibernate Session Bound to Thread вызывается в Hibernate 3 вместе с некоторым примером кода и как мы можем легко решить эту проблему. .