HibernateException: In Hibernate 3 ist keine Hibernate-Sitzung mit dem Thread verbunden

HibernateException: Keine Sitzung im Ruhezustand, die an den Thread in Ruhezustand 3 gebunden ist

1. Einführung

In diesem kurzen Tutorial werdenwe’ll clarify when “No Hibernate Session Bound to Thread” exception gets thrown and how to resolve it.

Wir konzentrieren uns hier auf zwei verschiedene Szenarien:

  1. unter Verwendung derLocalSessionFactoryBean

  2. unter Verwendung derAnnotationSessionFactoryBean

2. Die Ursache

Mit Version 3 führte Hibernate das Konzept der Kontextsitzung ein und die MethodegetCurrentSession()wurde der KlasseSessionFactoryhinzugefügt. Weitere Informationen zur kontextbezogenen Sitzung finden Sie unterhere.

Spring hat eine eigene Implementierung derorg.hibernate.context.CurrentSessionContext-Schnittstelle -org.springframework.orm.hibernate3.SpringSessionContext (im Fall von Spring Hibernate 3). __This implementation requires the session to be bound to a transaction.

Natürlich sollten Klassen, die die MethodegetCurrentSession()aufrufen, entweder auf Klassen- oder auf Methodenebene mit@Transactional versehen werden. Wenn nicht, werden dieorg.hibernate.HibernateException: No Hibernate Session Bound to Thread geworfen.

Schauen wir uns ein kurzes Beispiel an.

3. LocalFactorySessionBean __

Er ist das erste Szenario, das wir in diesem Artikel betrachten würden.

Wir werden eine Java Spring-Konfigurationsklasse mitLocalSessionFactoryBean definieren:

@Configuration
@EnableTransactionManagement
@PropertySource(
  { "classpath:persistence-h2.properties" }
)
@ComponentScan(
  { "org.example.persistence.dao", "org.example.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;
    }
    // ...
}

Beachten Sie, dass wir hier eine Hibernate-Konfigurationsdatei (exceptionDemo.cfg.xml) verwenden, um die Modellklasse zuzuordnen. Dies liegt daran, dassthe org.springframework.orm.hibernate3.LocalSessionFactoryBean does not provide the property*packagesToScan*, für die Zuordnung von Modellklassen verwendet werden.

Hier ist unser einfacher Service:

@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;

    // ...
 }

Wie wir im folgenden Codeausschnitt sehen können, wird diegetCurrentSession()-Methode derSessionFactory-Klasse verwendet, um die Hibernate-Sitzung zu erhalten:

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

Der folgende Test besteht und zeigt, wie die Ausnahme ausgelöst wird, wenn die KlasseEventService, die die Dienstmethode enthält, nicht mit einer@Transactional-Annotation versehen wird:

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

Dieser Test zeigt, wie die Dienstmethode erfolgreich ausgeführt wird, wenn die KlasseEventServicemit der Annotation@Transactional kommentiert wird:

@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

Diese Ausnahme kann auch auftreten, wenn wirorg.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean verwenden, umSessionFactory in unserer Spring-Anwendung zu erstellen.

Schauen wir uns einen Beispielcode an, der dies demonstriert. Insofern definieren wir eine Java Spring-Konfigurationsklasse mitAnnotationSessionFactoryBean:

@Configuration
@EnableTransactionManagement
@PropertySource(
  { "classpath:persistence-h2.properties" }
)
@ComponentScan(
  { "org.example.persistence.dao", "org.example.persistence.service" }
)
public class PersistenceConfig {
    //...
    @Bean
    public AnnotationSessionFactoryBean sessionFactory() {
        AnnotationSessionFactoryBean sessionFactory
          = new AnnotationSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setPackagesToScan(
          new String[] { "org.example.persistence.model" });
        sessionFactory.setHibernateProperties(hibernateProperties());

        return sessionFactory;
    }
    // ...
}

Mit derselben Gruppe von DAO-, Service- und Model-Klassen aus dem vorherigen Abschnitt tritt die oben beschriebene Ausnahme auf:

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

Wenn wir die Serviceklasse mit einer@Transactional-Annotation versehen, funktioniert die Service-Methode wie erwartet und der unten gezeigte Test besteht:

@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. Die Lösung

Es ist klar, dass diegetCurrentSession()-Methode derSessionFactory aus Spring innerhalb einer offenen Transaktion aufgerufen werden muss. Daher istthe solution is to ensure that our DAO/Service methods/classes are annotated correctly with the @Transactional annotation.

Es ist zu beachten, dass in Hibernate 4 und späteren Versionen die Meldung der Ausnahme, die aus demselben Grund ausgelöst wird, anders formuliert ist. Anstelle der "No Hibernate Session Bound to Thread”," erhalten wir "Could not obtain transaction-synchronized Session for current thread”."

Es gibt noch einen weiteren wichtigen Punkt. Zusammen mit derorg.hibernate.context.CurrentSessionContext-Schnittstelle hat Hibernate eine Eigenschafthibernate.current_session_context_class eingeführt, die auf die Klasse festgelegt werden kann, die den aktuellen Sitzungskontext implementiert.

Wie bereits erwähnt, verfügt Spring über eine eigene Implementierung dieser Schnittstelle:SpringSessionContext. Standardmäßig wird die Eigenschafthibernate.current_session_context_class auf diese Klasse gesetzt.

Wenn wir diese Eigenschaft explizit auf etwas anderes setzen, wird die Fähigkeit von Spring, die Sitzung und die Transaktionen im Ruhezustand zu verwalten, beeinträchtigt. Dies führt ebenfalls zu einer Ausnahme, unterscheidet sich jedoch von der betrachteten Ausnahme.

Zusammenfassend ist es wichtig zu bedenken, dass wir diehibernate.current_session_context_classnicht explizit festlegen sollten, wenn wir Spring zum Verwalten der Hibernate-Sitzung verwenden.

6. Fazit

In diesem Artikel haben wir uns angesehen, warum und wann die Ausnahmeorg.hibernate.HibernateException: No Hibernate Session Bound to Thread in Hibernate 3 ausgelöst wird, zusammen mit einem Beispielcode und wie wir sie leicht lösen können.

Der Code für diesen Artikel lautetover on Github.