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"));
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 .