1. введение
В этом коротком уроке мы уточним, когда возникает исключение “Нет сеанса гибернации, привязанного к потоку”, и как его устранить.
Мы сосредоточимся здесь на двух различных сценариях:
- использование LocalSessionFactoryBean
- использование 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 AbstractHibernateDaoimplements 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")); Listevents = 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")); Listevents = 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 .