HibernateException:Hibernate 3でスレッドにバインドされたHibernateセッションがない

1前書き

この短いチュートリアルでは、「スレッドにバインドされたHibernateセッションがない」という例外がスローされるタイミングとその解決方法を明確にします。

ここでは、2つの異なるシナリオに焦点を当てます。

  1. LocalSessionFactoryBean を使用する

  2. AnnotationSessionFactoryBean を使用する

2原因

バージョン3では、Hibernateはコンテキストセッションの概念を導入し、 getCurrentSession() メソッドが SessionFactory クラスに追加されました。コンテキストセッションの詳細については、https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/architecture-current-session.htmlを参照してください。

Springは独自の org.hibernate.context.CurrentSessionContext インターフェース - org.springframework.orm.hibernate3.SpringSessionContext (Spring Hibernate 3の場合)を実装しています。 __ ** この実装ではセッションをトランザクションにバインドする必要があります。

当然、 getCurrentSession() methodを呼び出すクラスには、クラスレベルまたはメソッドレベルで @ Transactional のアノテーションを付ける必要があります。そうでない場合は、 org.hibernate.HibernateException:No Hibernate Session to Thread がスローされます。

例を簡単に見てみましょう。

3 LocalFactorySessionBean

この記事では、彼が最初に検討するシナリオです。

LocalSessionFactoryBean を使用してJava Spring設定クラスを定義します。

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

   //...
 }

以下のコードスニペットからわかるように、 SessionFactory クラスの getCurrentSession() メソッドを使用してHibernateセッションを取得しています。

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

以下のテストに合格し、サービスメソッドを含むクラス 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.<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();
    }
}

4 AnnotationSessionFactoryBean

この例外は、Springアプリケーションで org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean を使用して SessionFactory を作成したときにも発生する可能性があります。

これを示すサンプルコードを見てみましょう。この範囲で、 AnnotationSessionFactoryBean を使用してJava Spring設定クラスを定義します。

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

        return sessionFactory;
    }
   //...
}

前のセクションと同じ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 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"));
    }
}

@ 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<Event> events = service.findAll();
    }
}

5ソリューション

Springから取得した SessionFactory getCurrentSession() メソッドは、開いているトランザクション内から呼び出す必要があることは明らかです。

そのため、** 解決策は、DAO/Serviceのメソッド/クラスに @ Transactional アノテーションを正しく付けることです。

Hibernate 4以降のバージョンでは、これと同じ理由でスローされる例外のメッセージの表現方法が異なります。 「__ スレッドにバインドされたHibernateセッションがありません」の代わりに、「 現在のスレッドに対してトランザクション同期セッションを取得できませんでした」と表示されます。

もう1つ重要なことがあります。 org.hibernate.context.CurrentSessionContext インターフェースとともに、Hibernateは現在のセッションコンテキストを実装するクラスに設定できるプロパティ hibernate.current session context class__を導入しました。

前述のように、Springにはこのインタフェースの独自の実装が付属しています。それは__SpringSessionContextです。

結果として、このプロパティを明示的に別のものに設定すると、Hibernateセッションとトランザクションを管理するSpringの機能が中断されます。これによっても例外が発生しますが、検討中の例外とは異なります。

まとめると、Springを使用してHibernateセッションを管理する場合は、 hibernate.current session context class__を明示的に設定しないでください。

6. 結論

この記事では、例外 org.hibernate.HibernateException:No Hibernate Session to Thread がHibernate 3でいくつかのサンプルコードと一緒にスローされる理由と、それを簡単に解決する方法を調べました。

この記事のコードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-hibernate3[over on Github]にあります。