I would like to get some clarification regarding lazy loading and session boundaries etc.
My code structure is as follows
@Entity
class A {
....
@OneToOne(fetch=LAZY)
private B b;
..
}
@Entity
class B {
private id;
private name;
}
@Transactional(SUPPORTS)
ADao {
A findById(int id);
}
@Transactional(SUPPORTS)
LayerDB {
A getAForId(int i) {
return adao.findById(i);
}
}
//Note that there is no transactional attribute here
LayerB {
public boolean doSomethingWithAandB(int aId) {
A a = LayerDB.getAForId(aId);
if(a.getB().getName().equals("HIGH"))
return true;
return false;
}
}
//start transaction here
@Transaction(REQUIRED)
LayerC {
LayerB layerb;
private handleRequest(int id) {
layerb.doSomethingWithAandB(id);
}
}
Now when we try to access B in entity A within the method
doSomethingWithAandB
Am getting a lazy initialization exception when trying to access B.
Even though the method is within the transaction created in LayerC, still i get the following exception
Exception : org.hibernate.LazyInitializationException: could not initialize proxy - no Session
But on changing the following two methods as :
@Transactional(SUPPORTS)
LayerDB {
A getAForId(int i) {
A a = adao.findById(i);
a.getB().getName();
return a;
}
}
//Note that there is no transactional attribute here
LayerB {
public boolean doSomethingWithAandB(int aId) {
A a = LayerDB.getAForId(aId);
if(a.getB().getName().equals("HIGH"))
return true;
return false;
}
}
Why is it not using the transaction / session created in LayerC ?
Even though we have SUPPORTS on the DBLayer, is it creating a separate ‘session’.
Any pointers for proper understand would help me a great deal.
Thank you.
With lazy loading, when you request an object a of type A, you get an object a of type A.
a.getB()however, will not be of type B, insteada.getB()is a proxy for B that can be resolved later on (that’s the lazy loading part), but only in the persistence context in which a lives in.Your second implementation does just that: it resolves B by calling
a.getB().getName()while you are still in the@Transaction. Hibernate can now make a second request to the database to fetch B, and nowa.getB()is really of type B and stays that way, so you can use it outside the persistence context.Your first implementation skips that. A is fetched from the database, the
@Transactionalblock ends, then you calla.getB().getName(), but now the persistence context is gone,a.getB()can not be fetched from the database, and an exception is thrown.