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

HibernateException:Hibernate 3のスレッドにバインドされたHibernateセッションはありません

1. 前書き

この短いチュートリアルでは、we’ll clarify when “No Hibernate Session Bound to Thread” exception gets thrown and how to resolve it.

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

  1. LocalSessionFactoryBeanを使用する

  2. AnnotationSessionFactoryBeanを使用する

2. 原因

バージョン3では、Hibernateはコンテキストセッションの概念を導入し、getCurrentSession()メソッドがSessionFactoryクラスに追加されました。 コンテキストセッションの詳細については、hereを参照してください。

Springには、org.hibernate.context.CurrentSessionContextインターフェース–org.springframework.orm.hibernate3.SpringSessionContextの独自の実装があります(Spring Hibernate 3の場合)。 __This implementation requires the session to be bound to a transaction.

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

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

3. LocalFactorySessionBean __

彼は、この記事で取り上げる最初のシナリオです。

LocalSessionFactoryBeanを使用してJavaSpring構成クラスを定義します。

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

モデルクラスをマップするために、ここではHibernate構成ファイル(exceptionDemo.cfg.xml)を使用することに注意してください。 これは、モデルクラスをマッピングするためのthe org.springframework.orm.hibernate3.LocalSessionFactoryBean does not provide the property*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
  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();
    }
}

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

このテストは、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 events = service.findAll();
    }
}

4. AnnotationSessionFactoryBean

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

これを示すサンプルコードを見てみましょう。 この点で、AnnotationSessionFactoryBeanを使用してJavaSpring構成クラスを定義します。

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

前のセクションと同じ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.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. ソリューション

Springから取得したSessionFactorygetCurrentSession()メソッドは、開いているトランザクション内から呼び出す必要があることは明らかです。 したがって、the solution is to ensure that our DAO/Service methods/classes are annotated correctly with the @Transactional annotation.

Hibernate 4以降のバージョンでは、これと同じ理由でスローされる例外のメッセージの表現が異なることに注意してください。 「No Hibernate Session Bound to Thread”,」の代わりに「Could not obtain transaction-synchronized Session for current thread”.」を取得します

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

前に述べたように、Springにはこのインターフェースの独自の実装が付属しています。SpringSessionContext.デフォルトでは、hibernate.current_session_context_classプロパティをこのクラスと同じに設定します。

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

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

6. 結論

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

この記事のコードはover on Githubにあります。