I have an instance of a class that I got from a Hibernate session. That session is long gone. Now, I'm calling toString()
and I'm getting the expected LazyInitializationException: could not initialize proxy - no Session
since I'm trying to access a reference which Hibernate didn't resolve during loading of the instance (lazy loading).
I don't really want to make the loading eager since it would change the query from about 120 characters to over 4KB (with eight joins). And I don't have to: All I want to display in toString()
is the ID of the referenced object; i.e. something that Hibernate needs to know at this point in time (or it couldn't do the lazy loading).
So my question: How do you handle this case? Never try to use references in toString()
? Or do you call toString()
in the loading code just in case? Or is there som开发者_开发问答e utility function in Hibernate which will return something useful when I pass it a reference which might be lazy? Or do you avoid references in toString()
altogether?
It's possible to do this by setting the accesstype of the ID field to "property". like:
@Entity
public class Foo {
// the id field is set to be property accessed
@Id @GeneratedValue @AccessType("property")
private long id;
// other fields can use the field access type
@Column private String stuff;
public long getId() { return id; }
public void setId(long id) { this.id = id; }
String getStuff() { return stuff; }
// NOTE: we don't need a setStuff method
}
It's explained here. This way the id field is allways populated when a proxy is created.
I've found a workaround:
public static String getId (DBObject dbo)
{
if (dbo == null)
return "null";
if (dbo instanceof HibernateProxy)
{
HibernateProxy proxy = (HibernateProxy)dbo;
LazyInitializer li = proxy.getHibernateLazyInitializer();
return li.getIdentifier ().toString ();
}
try
{
return Long.toString (dbo.id ());
}
catch (RuntimeException e)
{
return "???";
}
}
So what this code does is it fetches the ID (a 64bit number) from the object. DBObject
is an interface which defines long id()
. If the object is a Hibernate proxy, then I talk to its LazyInitializer
to get the ID. Otherwise, I call id()
. Usage:
class Parent {
DBObject child;
public String toString () {
return "Parent (id=..., child=" + getId(child)+")");
}
}
I've found the way that best fits good practice is a modification of the solution found on this blog: http://www.nonfunc.com/2016/02/05/jpa-performance-gotcha-tostring-really/. It needed modification for nullable fields and for Collections objects.
public toString() {
return String.format("FuBar [id=%d" +
+ ", fu=%s" // fu is a lazy non-nullable field
+ ", bar=%s" // bar is a nullable lazy field
+ ", borks=%s]", // borks is a lazy collection of Bork objects
id,
fu instanceof HibernateProxy ? "[null]" : fu.toString(),
bar == null || bar instanceof HibernateProxy ? "[null]" : bar.toString(),
borks instanceof PersistentSet ? "[null]" : borks.toString());
}
If all you want to return is the ID of the object, I imagine calling getID(), then parse the int/long as a String value at the moment you want it displayed would work just fine. At least that's how it seems based on the question.
EDIT
How to solve the LazyInitializationException using JPA and Hibernate
After viewing the comment and doing some searching I believe this may be most beneficial to your scenario.
精彩评论