HibernateException: Tiada Sesi Hibernate Terikat dengan Thread di Hibernate 3

Ketekunan atas

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

>> SEMAK KURSUS

1. Pengenalan

Dalam tutorial ringkas ini, kami akan menjelaskan bila pengecualian "Tanpa Hibernate Session Bound to Thread" dilemparkan dan bagaimana menyelesaikannya.

Kami akan memberi tumpuan di sini pada dua senario yang berbeza:

  1. menggunakan LocalSessionFactoryBean
  2. menggunakan AnnotationSessionFactoryBean

2. Sebabnya

Dengan versi 3, Hibernate memperkenalkan konsep sesi kontekstual dan kaedah getCurrentSession () ditambahkan ke kelas SessionFactory . Maklumat lebih lanjut mengenai sesi kontekstual boleh didapati di sini.

Spring mempunyai pelaksanaannya sendiri daripada org.hibernate.context.CurrentSessionContext antara muka - org.springframework.orm.hibernate3.SpringSessionContext (dalam hal Spring Hibernate 3). Pelaksanaan ini mengharuskan sesi terikat dengan transaksi.

Secara semula jadi, kelas yang memanggil kaedah getCurrentSession () harus dijelaskan dengan @Transactional sama ada di peringkat kelas atau tahap kaedah. Sekiranya tidak, org.hibernate.HibernateException: Tiada Hibernate Session Bound to Thread akan dilemparkan.

Mari kita lihat contohnya dengan cepat.

3. LocalFactorySessionBean

Dia adalah senario pertama yang akan kita lihat dalam artikel ini.

Kami akan menentukan 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 bahawa kami menggunakan fail konfigurasi Hibernate ( kecualiDemo.cfg.xml ) di sini untuk memetakan kelas model. Ini kerana yang org.springframework.orm.hibernate3.LocalSessionFactoryBean tidak menyediakan hartanah packagesToScan , untuk kelas model pemetaan.

Inilah perkhidmatan mudah 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 dapat kita lihat dalam coretan kod di bawah, kaedah 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(); } }

Ujian di bawah lulus, menunjukkan bagaimana pengecualian akan dilemparkan apabila kelas EventService yang mengandungi kaedah perkhidmatan tidak diberi penjelasan 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")); } }

Ujian ini menunjukkan bagaimana kaedah perkhidmatan berjaya dilaksanakan apabila 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 boleh berlaku semasa kita menggunakan org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean untuk membuat SessionFactory dalam aplikasi Spring kami.

Mari lihat beberapa contoh kod yang menunjukkan ini. Sejauh ini, kami menentukan 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 set kelas DAO, Servis dan Model yang sama dari bahagian sebelumnya, kami mengalami 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")); } }

Sekiranya kami memberi anotasi kelas perkhidmatan dengan anotasi @Transactional , kaedah perkhidmatan berfungsi seperti yang diharapkan dan ujian yang ditunjukkan di bawah lulus:

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

Sudah jelas bahawa kaedah getCurrentSession () dari SessionFactory yang diperoleh dari Spring perlu dipanggil dari dalam transaksi terbuka. Oleh itu, penyelesaiannya adalah untuk memastikan bahawa kaedah / kelas DAO / Perkhidmatan kami dianotasikan dengan betul dengan anotasi @Transactional .

Perlu diingatkan bahawa dalam versi Hibernate 4 dan yang lebih baru, mesej pengecualian yang dilemparkan untuk alasan yang sama ini berlainan perkataan. Daripada " Tidak Ada Sesi Hibernate Terikat ke Thread", kita akan mendapat " Tidak dapat memperoleh Sesi diselaraskan transaksi untuk utas semasa".

Ada satu lagi perkara penting untuk dibuat. Bersama dengan antara muka org.hibernate.context.CurrentSessionContext , Hibernate telah memperkenalkan hibernate.current_session_context_class harta yang boleh diatur ke kelas yang menerapkan konteks sesi semasa.

Seperti yang dinyatakan sebelumnya, Spring hadir dengan pelaksanaan antaramuka ini sendiri: SpringSessionContext. Secara lalai ia menetapkan harta hibernate.current_session_context_class sama dengan kelas ini.

Akibatnya, jika kita secara eksplisit menetapkan harta ini kepada sesuatu yang lain, ia mengganggu kemampuan Spring untuk mengurus sesi dan transaksi Hibernate. Ini menghasilkan pengecualian juga tetapi berbeza dengan pengecualian yang dipertimbangkan.

Ringkasnya, penting untuk diingat bahawa kita tidak boleh menetapkan kelas hibernate.current_session_context_class secara eksplisit ketika kita menggunakan Spring untuk menguruskan sesi Hibernate.

6. Kesimpulannya

Dalam artikel ini, kami melihat mengapa apabila pengecualian org.hibernate.HibernateException: No Hibernate Session Bound to Thread dilemparkan di Hibernate 3 bersama dengan beberapa kod contoh dan bagaimana kita dapat menyelesaikannya dengan mudah.

Kod untuk artikel ini boleh didapati di Github.

Ketekunan bawah

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

>> SEMAK KURSUS