HibernateException: nessuna sessione di ibernazione associata al thread in ibernazione 3

Persistenza in alto

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO

1. Introduzione

In questo breve tutorial, chiariremo quando viene generata l'eccezione "No Hibernate Session Bound to Thread" e come risolverla.

Ci concentreremo qui su due diversi scenari:

  1. utilizzando LocalSessionFactoryBean
  2. utilizzando AnnotationSessionFactoryBean

2. La causa

Con la versione 3, Hibernate ha introdotto il concetto di sessione contestuale e il metodo getCurrentSession () è stato aggiunto alla classe SessionFactory . Ulteriori informazioni sulla sessione contestuale sono disponibili qui.

Spring ha una propria implementazione dell'interfaccia org.hibernate.context.CurrentSessionContext - org.springframework.orm.hibernate3.SpringSessionContext (nel caso di Spring Hibernate 3). Questa implementazione richiede che la sessione sia associata a una transazione.

Naturalmente, le classi che chiamano il metodo getCurrentSession () dovrebbero essere annotate con @Transactional a livello di classe o di metodo. In caso contrario, verrà lanciata l' eccezione org.hibernate.HibernateException: No Hibernate Session Bound to Thread .

Diamo una rapida occhiata a un esempio.

3. LocalFactorySessionBean

È il primo scenario che vedremmo in questo articolo.

Definiremo una classe di configurazione Java Spring con 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; } // ... }

Nota che qui usiamo un file di configurazione di Hibernate ( exceptionDemo.cfg.xml ) per mappare la classe del modello. Questo perché l'org.springframework.orm.hibernate3.LocalSessionFactoryBean non fornisce la proprietà packagesToScan , per le classi del modello di mappatura.

Ecco il nostro semplice servizio:

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

Come possiamo vedere nel frammento di codice di seguito, il metodo getCurrentSession () della classe SessionFactory viene utilizzato per ottenere la sessione Hibernate:

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(); } }

Il test seguente viene superato, dimostrando come verrà generata l'eccezione quando la classe EventService contenente il metodo del servizio non è annotata con un'annotazione @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")); } }

Questo test mostra come il metodo del servizio viene eseguito correttamente quando la classe EventService viene annotata con l' annotazione @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

Questa eccezione può verificarsi anche quando utilizziamo org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean per creare SessionFactory nella nostra applicazione Spring.

Diamo un'occhiata ad un codice di esempio che lo dimostra. In questo senso, definiamo una classe di configurazione Java Spring con 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; } // ... }

Con lo stesso set di classi DAO, Service e Model della sezione precedente, incontriamo l'eccezione descritta sopra:

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

Se annotiamo la classe di servizio con un'annotazione @Transactional , il metodo di servizio funziona come previsto e il test mostrato di seguito viene superato:

@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. La soluzione

È chiaro che il metodo getCurrentSession () della SessionFactory ottenuto da Spring deve essere chiamato dall'interno di una transazione aperta. Pertanto, la soluzione è garantire che i nostri metodi / classi DAO / Service siano annotati correttamente con l' annotazione @Transactional .

Va notato che in Hibernate 4 e versioni successive, il messaggio dell'eccezione che viene lanciato per lo stesso motivo ha una formulazione diversa. Invece di " Nessuna sessione di ibernazione legata al thread", otterremmo " Impossibile ottenere la sessione sincronizzata con la transazione per il thread corrente".

C'è un altro punto importante da sottolineare. Insieme all'interfaccia org.hibernate.context.CurrentSessionContext , Hibernate ha introdotto una proprietà hibernate.current_session_context_class che può essere impostata sulla classe che implementa il contesto della sessione corrente.

Come affermato in precedenza, Spring viene fornito con la propria implementazione di questa interfaccia: SpringSessionContext. Per impostazione predefinita, imposta la proprietà hibernate.current_session_context_class uguale a questa classe.

Di conseguenza, se impostiamo esplicitamente questa proprietà su qualcos'altro, interrompe la capacità di Spring di gestire la sessione e le transazioni di Hibernate. Ciò si traduce anche in un'eccezione, ma è diverso dall'eccezione in esame.

Riassumendo, è importante ricordare che non dovremmo impostare esplicitamente hibernate.current_session_context_class quando usiamo Spring per gestire la sessione di Hibernate.

6. Conclusione

In questo articolo, abbiamo esaminato il motivo per cui l'eccezione org.hibernate.HibernateException: No Hibernate Session Bound to Thread viene lanciata in Hibernate 3 insieme ad un codice di esempio e come possiamo risolverlo facilmente.

Il codice per questo articolo può essere trovato su Github.

Fondo di persistenza

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO