org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Everyone who has ever used Hibernate instantly recognizes the problem. Smart proxy that Hibernate uses to load child entities of an entity on-demand could not perform its duty as the entity instance had already escaped the data layer and session had been closed. Child objects could be loaded eagerly in the data layer to make problem go away.
I examined the code and clearly child objects whose attempted retrieval resulted in the exception were never loaded. Weirdly, no-one else received exception on that page. The web layer code where exception occurred was roughly as this:
// Q, QLabel & Language are Hibernate entitiesThe data layer code that loaded the main entity was bad Hibernate usage, manually forcing the n+1 selects
private String getQTitleInLang(Q q, String langCode) {
for (QLabel ql: q.getLabels()) {
// QLabel's Language is uninitialized, getCode() fails
if(langCode != null && ql.getLanguage().getCode().equals(langCode)){
return ql.getDescription();
}
}
return null;
}
private void initQ(Set<q> qs) throws HibernateException {Correct but bad fix is to add the nasty loop that initializes QLabels:
for (Q q: qs) {
Hibernate.initialize(q.getLanguages());
Hibernate.initialize(q.getLabels());
}
}
private void initQ(Set<q> qs) throws HibernateException {So why did the code work for everyone else? It was just that they happened to work with different database. In their databases all the Language entities that QLabels' owned coincided with those that Q itself had as its child Languages.
for (Q q: qs) {
Hibernate.initialize(q.getLanguages());
Hibernate.initialize(q.getLabels());
// The ugly fix. In real code that brings 3rd nested for loop.
for (QLabel ql: q.getLabels()) {
Hibernate.initialize(ql.getLanguage());
}
}
}
As Hibernate Session holds a mandatory first-level cache of persistent objects that are used when navigating the object graph or looking up objects by identifier, that made the "unloaded" entities accessible under certain conditions.
Session-level caching just happens to work so well that code coincidentally works sometimes :)
No comments:
Post a Comment