Umpamakan skenario berikut ini. Ada dua entity yang mempunyai relasi parent-child dan multiplicity one-to-many. Entity Order mempunyai beberapa OrderItem.
Order 1 —-> 0..* OrderItem
Class untuk entity Order kira-kira seperti berikut.
@Entity
@Table(name = "table_order")
public class Order implements java.io.Serializable {
private Long id;
private List<OrderItem> orderItems;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orders) {
orderItems = orders;
}
}
Relasi dari Order ke OrderItem diwakili oleh property “orderItems” yang merupakan sebuah Collection. Pada saat aplikasi dijalankan, code berikut bisa jadi akan melemparkan LazyInitializationException.
order.getOrderItems().get(0);
Atau bisa juga code berikut ini.
for(OrderItem item:order.getOrderItems()) {
// do something..
}
Kedua code diatas melakukan hal yang sama: mengakses relasi dari sebuah objek entity.
Jadi kenapa Anda mendapatkan LazyInitializationException?
Ada kalanya relasi dari sebuah entity melibatkan ribuan baris data. Dalam kasus kita, misalkan ada beberapa Order yang memiliki OrderItem sebanyak lebih dari 100 ribu. Ini tentu akan mempengaruhi loading time, dan akan sia-sia kalau kita tidak menggunakan data dari relasi tersebut. Hibernate mempunyai cara untuk menyelesaikan hal ini.
Secara default, relasi dari sebuah entity adalah lazy (kecuali kita menyatakan sebaliknya. Ini disebut fetching strategy. Untuk konfigurasi dan cara-cara untuk setting fetching strategy/eagerness silakan lihat disini dan disini). Artinya, relasi tersebut tidak akan di load (tidak disertakan dalam query) pada saat entity induk diload dari database. Relasi tersebut akan diinisiasi pada saat dibutuhkan (pada saat diakses pertama kali) . Pada contoh kasus kita, pada saat order di load pertama kali,berarti hanya Order saja yang diload, sedangkan OrderItem tidak akan di load sampai kita mengakses relasi tersebut (misal order.getOrderItems().get(0) ).
Cara Hibernate melakukan hal tersebut adalah seperti berikut. Umpamakan code berikut ini.
Order order = getOrder(); order.getId(); order.getOrderItems().get(0);
Pada saat runtime, objek entity yang ditunjuk oleh suatu reference (variable) bisa jadi bukan kelas asli dari objek tersebut, tetapi suatu kelas turunan yang disebut proxy. Jadi dalam kasus kita, variable “order” sebenarnya menunjuk ke suatu objek yang classnya bukanĀ “Order”, tetapi “OrderProxy” (bukan nama class sebenarnya, hanya untuk ilustrasi). OrderProxy ini kebanyakan hanya mendelegasikan pemanggilan method ke objek yang asli. Kecuali untuk method yang mengembalikan suatu relasi yang sifatnya lazy. (…bingung…? tahan sebentar..)
Pada keadaan normal (dimana kita tidak menggunakan Hibernate dan tidak ada proxy yang terlibat), code diatas skemanya adalah seperti dalam diagram berikut (dimulai dari line 2).

Sedangkan bila kita menggunakan Hibernate, maka skemanya akan jadi “sedikit” lebih rumit.

(Disclaimer: beberapa nama kelas diatas bukan nama yang sesungguhnya. Nama-nama tersebut adalah karangan penulis sekedar untuk menyederhanakan ilustrasi)
Sekarang ada sebuah proxy diantara current thread dan object Order. Proxy tersebut akan mencegat semua pemanggilan method. Bila method tersebut tidak melibatkan akses ke relasi yang lazy, maka pemanggilan method tersebut akan diteruskan ke objek Order yang asli. Bila suatu pemanggilan method melibatkan akses ke suatu relasi yang sifatnya lazy, maka prosedur untuk inisiasi relasi tersebut akan dilakukan. Didalam prosedur tersebut salah satunya adalah melakukan query ke database (untuk meload OrderItem). Untuk melakukan ini, maka Hibernate berusaha mendapatkan referensi ke persistence context (EntityManager) yang bertanggung jawab terhadap EntityTersebut (gampangnya, EntityManager yang meload entity tersebut). Prosedur tersebut ditandai dengan warna merah pada diagram diatas. Bila persistence context yang terkait dengan entity tersebut telah berakhir (atau tidak dapat dijangkau), maka Hibernate tidak dapat melakukan query ke database, dan pada akhirnya gagal untuk melakukan inisiasi relasi yang lazy diatas. Pada saat inilah Anda akan mendapatkan LazyInitializationException.
Entity yang tidak mempunyai persistence context (tidak termanage) ini disebut dengan istilah detached entity.
Jadi bagaimana cara mencegahnya?
Yang harus Anda lakukan adalah mengaitkan detached entity kepada suatu persistence context. Jika Anda menggunakan Hibernate API, salah satu dari line dibawah ini dapat digunakan.
session.update(order); // gunakan bila Anda sekaligus hendak mengupdate session.merge(order); // gunakan bila Anda sekaligus hendak mengupdate, tapi hati2 karena optimistic locking tidak akan bekerja jika Anda menggunakan fungsi ini session.lock(order,LockType.NONE); // ini cara yang paling benar
Bila Anda menggunakan JPA, lakukan seperti berikut.
order = entityManager.merge(order);
Perhatikan bahwa kita perlu menampung nilai kembalian dari merge(), karena fungsi tersebut membuat sebuah objek baru di heap. Objek yang lama akan tetap dalam keadaan detached.
Semoga bermanfaat.