Hibernate tidak dapat menginisialisasi proxy - tidak ada Sesi

1. Ikhtisar

Bekerja dengan Hibernate, kami mungkin mengalami kesalahan yang mengatakan: org.hibernate.LazyInitializationException: tidak dapat menginisialisasi proxy - tidak ada Sesi .

Dalam tutorial singkat ini, kita akan melihat lebih dekat akar penyebab kesalahan dan mempelajari cara menghindarinya.

2 Memahami Kesalahan

Akses ke objek yang lambat dimuat di luar konteks sesi Hibernate terbuka akan menghasilkan pengecualian ini.

Penting untuk memahami apa itu Session , Lazy Initialisation, dan Proxy Object dan bagaimana mereka bersatu dalam framework Hibernate .

  • Sesi adalah konteks ketekunan yang merepresentasikan percakapan antara aplikasi dan database
  • Lazy Loading artinya objek tidak akan dimuat ke konteks Sesi hingga diakses dalam kode.
  • Hibernate membuat subclass Proxy Object dinamis yang akan mencapai database hanya saat kita pertama kali menggunakan objek tersebut.

Kesalahan ini berarti bahwa kami mencoba untuk mengambil objek yang lambat dimuat dari database dengan menggunakan objek proxy, tetapi sesi Hibernate sudah ditutup.

3. Contoh untuk LazyInitializationException

Mari kita lihat pengecualian dalam skenario konkret.

Kami ingin membuat objek Pengguna sederhana dengan peran terkait. Mari gunakan JUnit untuk mendemonstrasikan kesalahan LazyInitializationException .

3.1. Kelas Hibernate Utility

Pertama, mari kita definisikan kelas HibernateUtil untuk membuat SessionFactory dengan konfigurasi.

Kami akan menggunakan database HSQLDB dalam memori .

3.2. Entitas

Inilah entitas Pengguna kami :

@Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int id; @Column(name = "first_name") private String firstName; @Column(name = "last_name") private String lastName; @OneToMany private Set roles; } 

Dan entitas Peran terkait :

@Entity @Table(name = "role") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int id; @Column(name = "role_name") private String roleName; }

Seperti yang bisa kita lihat, ada hubungan satu-ke-banyak antara Pengguna dan Peran .

3.3. Membuat Pengguna dengan Peran

Selanjutnya, mari buat dua objek Role :

Role admin = new Role("Admin"); Role dba = new Role("DBA");

Kemudian, kami membuat Pengguna dengan peran:

User user = new User("Bob", "Smith"); user.addRole(admin); user.addRole(dba);

Akhirnya, kita bisa membuka sesi dan menahan objek:

Session session = sessionFactory.openSession(); session.beginTransaction(); user.getRoles().forEach(role -> session.save(role)); session.save(user); session.getTransaction().commit(); session.close();

3.4. Mengambil Peran

Dalam skenario pertama, kita akan melihat cara mengambil peran pengguna dengan cara yang tepat:

@Test public void whenAccessUserRolesInsideSession_thenSuccess() { User detachedUser = createUserWithRoles(); Session session = sessionFactory.openSession(); session.beginTransaction(); User persistentUser = session.find(User.class, detachedUser.getId()); Assert.assertEquals(2, persistentUser.getRoles().size()); session.getTransaction().commit(); session.close(); }

Di sini, kami mengakses objek di dalam sesi, oleh karena itu tidak ada kesalahan.

3.5. Kegagalan Mengambil Peran

Dalam skenario kedua, kita akan memanggil metode getRoles di luar sesi:

@Test public void whenAccessUserRolesOutsideSession_thenThrownException() { User detachedUser = createUserWithRoles(); Session session = sessionFactory.openSession(); session.beginTransaction(); User persistentUser = session.find(User.class, detachedUser.getId()); session.getTransaction().commit(); session.close(); thrown.expect(LazyInitializationException.class); System.out.println(persistentUser.getRoles().size()); }

Dalam hal ini, kami mencoba mengakses peran setelah sesi ditutup, dan akibatnya, kode akan melontarkan LazyInitializationException .

4. Bagaimana Menghindari Error

Mari kita lihat empat solusi berbeda untuk mengatasi kesalahan tersebut.

4.1. Buka Sesi di Lapisan Atas

Praktik terbaiknya adalah membuka sesi di lapisan persistensi, misalnya menggunakan Pola DAO.

Kita dapat membuka sesi di lapisan atas untuk mengakses objek terkait dengan cara yang aman. Misalnya, kita bisa membuka sesi di layer View .

Akibatnya, kita akan melihat peningkatan waktu respons, yang akan memengaruhi kinerja aplikasi.

Solusi ini merupakan anti-pola dalam hal prinsip Separation of Concerns. Selain itu, dapat menyebabkan pelanggaran integritas data dan transaksi yang berjalan lama.

4.2. Mengaktifkan Properti enable_lazy_load_no_trans

Properti Hibernate ini digunakan untuk mendeklarasikan kebijakan global untuk pengambilan objek yang lambat dimuat.

Secara default, properti ini salah . Mengaktifkannya berarti bahwa setiap akses ke entitas yang dimuat lambat terkait akan digabungkan dalam sesi baru yang berjalan dalam transaksi baru:

Using this property to avoid LazyInitializationException error is not recommended since it will slow down the performance of our application. This is because we'll end up with an n + 1 problem. Simply put, that means one SELECT for the User and N additional SELECTs to fetch the roles of each user.

This approach is not efficient and also considered an anti-pattern.

4.3. Using FetchType.EAGER Strategy

We can use this strategy along with a @OneToMany annotation, for example :

@OneToMany(fetch = FetchType.EAGER) @JoinColumn(name = "user_id") private Set roles;

This is a kind of compromised solution for a particular usage when we need to fetch the associated collection for most of our use cases.

So it's much easier to declare the EAGER fetch type instead of explicitly fetching the collection for most of the different business flows.

4.4. Using Join Fetching

We can use a JOIN FETCH directive in JPQL to fetch the associated collection on-demand, for example :

SELECT u FROM User u JOIN FETCH u.roles

Or we can use the Hibernate Criteria API :

Criteria criteria = session.createCriteria(User.class); criteria.setFetchMode("roles", FetchMode.EAGER);

Here, we specify the associated collection that should be fetched from the database along with the User object on the same round trip. Using this query improves the efficiency of iteration since it eliminates the need for retrieving the associated objects separately.

This is the most efficient and fine-grained solution to avoid the LazyInitializationException error.

5. Conclusion

Dalam artikel ini, kami melihat cara menangani org.hibernate.LazyInitializationException: tidak dapat menginisialisasi proxy - tidak ada kesalahan Sesi .

Kami menjelajahi berbagai pendekatan bersama dengan masalah kinerja. Penting untuk menggunakan solusi yang sederhana dan efisien agar tidak memengaruhi kinerja.

Terakhir, Kami melihat bagaimana pendekatan join-fetching adalah cara yang baik untuk menghindari kesalahan.

Seperti biasa, kode tersedia di GitHub.