HibernateException: No Hibernate Session Bound to Thread in Hibernate 3

Ketekunan teratas

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya

1. Perkenalan

Dalam tutorial singkat ini, kami akan mengklarifikasi kapan pengecualian "Tidak Ada Sesi Hibernasi yang Terikat ke Untaian" dan cara mengatasinya.

Kami akan fokus di sini pada dua skenario berbeda:

  1. menggunakan LocalSessionFactoryBean
  2. menggunakan AnnotationSessionFactoryBean

2. Penyebabnya

Dengan versi 3, Hibernate memperkenalkan konsep sesi kontekstual dan metode getCurrentSession () ditambahkan ke kelas SessionFactory . Informasi lebih lanjut tentang sesi kontekstual dapat ditemukan di sini.

Spring memiliki implementasi sendiri dari antarmuka org.hibernate.context.CurrentSessionContext - org.springframework.orm.hibernate3.SpringSessionContext (dalam kasus Spring Hibernate 3). Implementasi ini membutuhkan sesi untuk diikat ke sebuah transaksi.

Biasanya, kelas yang memanggil metode getCurrentSession () harus dianotasi dengan @Transactional baik di tingkat kelas atau tingkat metode. Jika tidak, org.hibernate.HibernateException: No Hibernate Session Bound to Thread akan dilempar.

Mari kita lihat contohnya.

3. LocalFactorySessionBean

Dia skenario pertama yang akan kita lihat di artikel ini.

Kami akan mendefinisikan kelas konfigurasi Java Spring dengan 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; } // ... }

Perhatikan bahwa kami menggunakan file konfigurasi Hibernate ( exceptionDemo.cfg.xml ) di sini untuk memetakan kelas model. Hal ini karena para org.springframework.orm.hibernate3.LocalSessionFactoryBean tidak menyediakan properti packagesToScan , untuk kelas model pemetaan.

Inilah layanan sederhana kami:

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

Seperti yang bisa kita lihat pada potongan kode di bawah ini, metode getCurrentSession () dari kelas SessionFactory digunakan untuk mendapatkan sesi 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(); } }

Pengujian di bawah ini berhasil, mendemonstrasikan bagaimana pengecualian akan dilempar ketika kelas EventService yang berisi metode layanan tidak dianotasi dengan anotasi @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")); } }

Pengujian ini menunjukkan bagaimana metode layanan berhasil dijalankan ketika kelas EventService dianotasi dengan anotasi @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

Pengecualian ini juga dapat terjadi saat kita menggunakan org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean untuk membuat SessionFactory di aplikasi Spring kita.

Mari kita lihat beberapa contoh kode yang menunjukkan hal ini. Sejauh ini, kami mendefinisikan kelas konfigurasi Java Spring dengan 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; } // ... }

Dengan sekumpulan kelas DAO, Layanan, dan Model yang sama dari bagian sebelumnya, kami menemukan pengecualian seperti yang dijelaskan di atas:

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

Jika kita menganotasi kelas layanan dengan anotasi @Transactional , metode layanan akan bekerja seperti yang diharapkan dan pengujian yang ditunjukkan di bawah ini berhasil:

@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. Solusi

Jelas bahwa metode getCurrentSession () dari SessionFactory yang diperoleh dari Spring perlu dipanggil dari dalam transaksi terbuka. Oleh karena itu, solusinya adalah memastikan bahwa metode / kelas DAO / Layanan kami dianotasi dengan benar dengan anotasi @Transactional .

Perlu dicatat bahwa dalam Hibernate 4 dan versi yang lebih baru, pesan pengecualian yang muncul karena alasan yang sama ini menggunakan kata yang berbeda. Alih-alih " Tidak Ada Sesi Hibernasi yang Terikat ke Utas", kami akan mendapatkan " Tidak dapat memperoleh Sesi yang disinkronkan dengan transaksi untuk utas saat ini".

Ada hal penting lain yang harus dibuat. Seiring dengan antarmuka org.hibernate.context.CurrentSessionContext , Hibernate telah memperkenalkan properti hibernate.current_session_context_class yang dapat disetel ke kelas yang mengimplementasikan konteks sesi saat ini.

Seperti yang dinyatakan sebelumnya, Spring hadir dengan implementasi antarmuka ini sendiri: SpringSessionContext. Secara default menyetel properti hibernate.current_session_context_class sama dengan kelas ini.

Akibatnya, jika kita secara eksplisit mengatur properti ini ke sesuatu yang lain, itu mengganggu kemampuan Spring untuk mengelola sesi dan transaksi Hibernate. Ini menghasilkan pengecualian juga, tetapi berbeda dari pengecualian yang dipertimbangkan.

Meringkas, penting untuk diingat bahwa kita tidak boleh menyetel hibernate.current_session_context_class secara eksplisit saat kita menggunakan Spring untuk mengelola sesi Hibernate.

6. Kesimpulan

Pada artikel ini, kita melihat mengapa ketika pengecualian org.hibernate.HibernateException: No Hibernate Session Bound to Thread dilemparkan dalam Hibernate 3 bersama dengan beberapa contoh kode dan bagaimana kita dapat menyelesaikannya dengan mudah.

Kode untuk artikel ini dapat ditemukan di Github.

Ketekunan bawah

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya