In Hibernate we have two classes with the following classes with JPA mapping:
package com.example.hibernate
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class Foo {
private long id;
private Bar bar;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.LAZY)
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
package com.example.hibernate
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
public class Bar {
private long id;
private String title;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Now when we load from the database an object from class Foo using session get e.g:
Foo foo = (Foo)session.get(Foo.class, 1 /* or some other id that exists in the DB*/); the Bar member of foo is a proxy object (in our case javassist proxy but it can be cglib one depending on the bytecode provider you use), that is not initialized. If you then use session.get to fetch the Bar object that is the member of the Foo class just loaded (we are in the same session), Hibernate does not issue another DB query and fetches the object from the session (first level) cache. The problem is this is a proxy to Bar class which is not initialized and trying to call this object getId() will return 0, and getTitle() will return null. Our current solution is pretty ugly and checks if the object returned from get is a proxy here is the code (form a generic DAO implementation):
@SuppressWarnings("unchecked")
@Override
@Transactional(readOnly = true)
public <T extends IEntity> T get(Class<T> clazz, Serializable primaryKey) throws DataAccessException {
T entity = (T) currentSession().get(clazz, primaryKey);
if (entity != null) {
if (LOG.isWarnEnabled()) {
LOG.warn("Object not found for class " + clazz.getName() + " with primary key " + primaryKey);
}
} else if (entity instanceof HibernateProxy){ // TODO: force initialization due to Hibernate bug
HibernateProxy proxy = (HibernateProxy)entity;
if (!Hibernate.isInitialized(proxy)) {
Hibernate.initialize(proxy);
}
entity = (T)proxy.getHibernateLazyInitializer().getImplementation();
}
return entity;
}
Is there a better way to do this, couldn't find a solution in the Hibernate forum, and didn't find the issue in Hibernate's JIRA.
Note: we cannot just use foo.getBar() (which will initialize the proxy properly) to get the Bar class object, because the session.get operation to fetch the Bar object does not know (or care for that matter) that the Bar class is also a lazy member of a Foo object that was just fetched.