Monday, June 16, 2014

Hibernate LazyInitialization Exception

Jika kita menggunakan Hibernate ORM framework bersama dengan JPA tentu kita tidak asing dengan keyword LAZY. Biasanya digunakan pada relationship di suatu domain atau entity object.
Jika suatu relasi mengunakan fetch Lazy, maka relasi pada suatu entity tidak akan diGet datanya secara langsung pada saat query, melainkan saat dipanggil (get objectnya) saja baru dia diQuery. Maka dinamakan LAZY Initialization.
Secara default suatu relasi jika tidak didefinisikan fetchnya otomatis akan LAZY.
LAZY mempunyai keuntungan jika jumlah data si object relasi itu cukup besar, tidak akan diload secara langsung dan tidak membebani query.

Berikut ini contohnya :


Kita memiliki entity Employee dan Address yang saling berelasi.

 Class Employee {  
      @Id       
      private Integer id;  
      private String firstname;  
      private String lastname;  
      private String phone;  
      @OneToMany(mappedBy = "addressId", targetEntity = Address.class ,cascade = CascadeType.ALL, orphanRemoval = true)  
      private Set<Address> address ;  
      // settes + getter  
 }  

 Class Address {  
      @Id  
      private Integer id;  
      private String street;  
      private String city;  
      private Integer postal;  
      @ManyToOne  
      @JoinColumn(name = "ADDRESS_ID", insertable = true, updatable = true)  
      private Address addressId;  
      // setter + getter  
 }  

Kemudian kita punya script query di class DAO :

 public List<Address> getAddress() {  
   Query query = EntityManager.createQuery(“select e from Employee e where e.id=1”);  
   Employee employee = query.getSingleResult();  
 /// disini baru diquery : select e from employee e where e.id=1   
    List<Address> listAddress = employee.getAddress();   
 /// pada saat di getAddress() maka object address baru diquery;  
 /// select a from address a where a.address_id=…  
 /// Get Exception  
 }  

Nah disinilah akan muncul error exception (Hibernate LazyInitializationException), seperti ini :

 org.hibernate.LazyInitializationException: could not initialize proxy - no Session  
   at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86)  
   at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140)  
   at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)  
   at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.java)  
   at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.java:73)  
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)  
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)  
   at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)...... 

Hal ini disebabkan karena query menggunakan HibernateSession. Nah setelah query di getResult(). Session otomatis disclose oleh JPA EntityManager.
Pada saat akan melakukan getAddress() tentu akan membutuhkan HibernateSession juga untuk melakukan query ke database.
Karena HibernateSession sudah terlanjur di close(), muncul exception diatas; could not initialize proxy – no Session at…

Biasanya muncul pada relasi OneToMany dan ManyToMany.

Bagaimana cara mengatasinya ?

1. Fetch di relasi diubah jadi EAGER.

Artinya object relasi akan di get langsung pada saat query dijalankan. Cara ini tidak direkomendasikan karena jika data dari object relasi jumlahnya besar, query akan menjadi berat.
 @OneToMany(mappedBy = "addressId", targetEntity = Addresss.class ,cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)  
 private Set<Address> address ;  

2. Memakai class native hibernateSession pada saat query. Jadi bisa diatur kapan session open kapan session close. Tapi kekurangannya jika JPA providernya diganti bukan Hibernate lagi. Maka akan butuhkan banyak perubahan di code kita. Mungkin cara ini bisa dipakai di code kita menggunakan Hibernate murni tanpa JPA.


 Session session = getHibernateSession();  
     Query query = session.getNamedQuery("Employee.getEmployeeById");  
     query.setParameter("ID", employeeId);  
     Expense result = (Employee) query.uniqueResult();  
     if(result != null){  
       Hibernate.initialize(result.getAddress());  
       return result;  
     }  
     session.close();  

3. Menggunakan openEntityManagerFilter di WEB.XML. Session akan dimaintain oleh filter ini. Maka jika ada request yang membutuhkan hibernateSession, maka entity filter tersebut akan membukakan session ke database. Cara ini paling direkomendasikan.

 <!-- hibernate session filter -->  
   <filter>  
     <filter-name>OpenEntityManagerInViewFilter</filter-name>  
     <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>  
   </filter>  
   <filter-mapping>  
     <filter-name>OpenEntityManagerInViewFilter</filter-name>  
     <url-pattern>/*</url-pattern>  
   </filter-mapping>  

Selamat mencoba. :)

No comments:

Post a Comment