How to avoid Hibernate Lazy Initialization Exception
I have got the Lazy Initialization Exception problem so many times that I have decided to post everything I have learnt about it.
During my research, I have found 3 ways to avoid Hibernate Lazy Initialization Exception:
- Set the lazy property to false (not recommended)
- Keep the session open
- Eagerly fetch the associations
Setting the Lazy Property to false
In the mapping file you can set the lazy property to false. Then, every time you query this class you will retrieve the object and the associated graph of objects.
I don’t recommend this approach because it will increment the database load and therefore, it will produce a decrease in performance.
Eagerly fetch
From my point of view this is the best solution to avoid the lazy initialization problem. In HQL, you just need to add the fetch keyword in the from clause to eagerly fetch an association.
Here is an example:
from Doctor doc
left join fetch doc.patients
where doc.name like ‘Doctor A%’
In this example, we retrieve the Doctor object and all the patients related to this doctor.
You could declare in the mapping file the eager fetching by default, but I will suggest to the use it only at runtime for a particular HQL query, otherwise your will increase unnecessarily database load.
Keep the session open
The last approach, consist in keeping the session open until the request is done. If the session is open during the request you could get the associated graph but you need to be sure that the action takes within the same transaction. In this case, the developer needs to handle possible transaction errors.
Examples
Using JUnit test I will show some examples. You can find the whole project in github.
Model
In my model I have Specialties, Doctors and Patients. The associations between them are:
1 Specialty can have 0 or more Doctors
1 Doctor only can belong to 1 Specialty
1 Doctor can have 0 or more Patients
1 Patient only can have 1 doctor
In this post, I’m not going to explain how to configure each class in hibernate but you can find it here: Specialty, Doctor and Patient.
Lazy Initialization Exception
I have created a Junit test that will run a query producing a Lazy Initialization Exception.
First of all, I will load some test date. I use the @BeforeClass annotation, so the init method is executed once, before the start of all tests.
[java]
@BeforeClass
public static void init(){
ApplicationContext ctx= new ClassPathXmlApplicationContext(“applicationContext.xml”);
Specialty specialty = new Specialty(“Specialty”);
List specialties = new ArrayList();
specialties.add(specialty);
Doctor doctorA = new Doctor (“Doctor A”, specialty);
Patient patientA = new Patient(“Patient A”, doctorA);
Patient patientB = new Patient(“Patient B”, doctorA);
List patients = new ArrayList();
patients.add(patientA);
patients.add(patientB);
service = (IMedicalDao) ctx.getBean(“medicalDao”);
service.savePatients(patients);
}
@Test(expected = LazyInitializationException.class)
public void getLazyInitializationException(){
Specialty specialty = service.getSpecialtyByName(“Specialty”);
System.out.println(“Specialty ID: ” + specialty.getId() + ” Name: ” + specialty.getName());
//This call will throw a Lazy Initialization Exception
Doctor d = specialty.getDoctorList().iterator().next();
}
[/java]
And here is the Query:
[java]
public Specialty getSpecialtyByName(String name) {
Session session =sessionFactory.openSession();
Query query = session.createQuery(“From Specialty where name =:nameSpec”);
query.setParameter(“nameSpec”, name);
Specialty specialty = (Specialty) query.uniqueResult();
session.close();
return specialty;
}
[/java]
Note that this query is only retrieving the Specialty Object and not the whole associated Objects, so when we try to access to the doctorList we get a Lazy Initialization Exception.
This is the output that we get from the test:
Specialty ID: 1 Name: Specialty
2013-09-04 13:18:15,299 ERROR [org.hibernate.LazyInitializationException] –
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.hibernateTest.domain.Specialty.doctorList, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)…
It throws the exception but the test pass because we have added the @Test(expected = LazyInitializationException.class) annotation.
Eagerly fetch
As I said before, you just need to add the fetch keyword to eagerly fetch and association. In this example we will look for a specific Specialty, and we will retive also all the Doctors and Patients that belongs to that Specialty
[java]
public Specialty getSpecialtyByNameEagerlyFetched(String name) {
Session session =sessionFactory.openSession();
Query query = session.createQuery(“From Specialty s ” +
“left join fetch s.doctorList d ” +
“left join fetch d.patientList ” +
“where name =:nameSpec”);
query.setParameter(“nameSpec”, name);
Specialty specialty = (Specialty) query.uniqueResult();
session.close();
return specialty;
}
[/java]
And here is the test that runs this query:
[java]
@Test
public void testEagerlyFetched(){
Specialty specialty = service.getSpecialtyByNameEagerlyFetched(“Specialty”);
System.out.println(“Specialty ID: ” + specialty.getId() + ” Name: ” + specialty.getName());
Doctor doctor = specialty.getDoctorList().iterator().next();
System.out.println(“Doctor ID: ” + doctor.getId() + ” Name: ” + doctor.getName());
Patient patient = doctor.getPatientList().iterator().next();
System.out.println(“Patient ID: ” + patient.getId() + ” Name: ” + patient.getName());
}
[/java]
Here is the output:
Specialty ID: 1 Name: Specialty
Doctor ID: 1 Name: Doctor A
Patient ID: 2 Name: Patient B
Keep the session open
In this case the query, will be exacly the same than in the original case but with the only difference that we are not clossing the session this time.
[java]
public Specialty getSpecialtyByNameOpenSession(String name) {
Session session =sessionFactory.openSession();
Query query = session.createQuery(“From Specialty where name =:nameSpec”);
query.setParameter(“nameSpec”, name);
Specialty specialty = (Specialty) query.uniqueResult();
//session.close();
return specialty;
}
[/java]
Note that the session.close is commented.
Here is the test that use this function:
[java]
@Test
public void testOpenSession(){
Specialty specialty = service.getSpecialtyByNameOpenSession(“Specialty”);
System.out.println(“Specialty ID: ” + specialty.getId() + ” Name: ” + specialty.getName());
Doctor doctor = specialty.getDoctorList().iterator().next();
System.out.println(“Doctor ID: ” + doctor.getId() + ” Name: ” + doctor.getName());
Patient patient = doctor.getPatientList().iterator().next();
System.out.println(“Patient ID: ” + patient.getId() + ” Name: ” + patient.getName());
}
[/java]
And the output is exactly the same as in the previous test:
Specialty ID: 1 Name: Specialty
Doctor ID: 1 Name: Doctor A
Patient ID: 2 Name: Patient B
is there a way do not fetch eager and do not keep the session?
what i want is
just return null, and do not query the database,
is there a way to realize that?
I am not sure I understand your question. If you return a null value then you will get a Null Pointer Exception at the same point where you are getting now a Lazy initializaiton exception. Could you please try to explain to me what your problem is?