I've learned that Hibernate does not return an instance of your actual entity class when it gives you the result of a query, but instead returns a 'proxy' instance that is dynamically sub-classed from your actual entity's class. I understand the reason for this behaviour, being that it allows for the realization of the lazy initialization. However, I have a few questions left unanswered on the details of the implementation of these proxy classes:
Will the lazy fetched field get loaded ONLY when I use the getter? What if I use the field in, say, my equals
or hashCode
method? Will the execution of these methods result in a NullPointerException
when I have not called the getter of this field before?
How exactly does Hibernate initialize the field when its initialization is triggered? Does it execute the field's setter method that I have defined in the entity class, or will it assign the value directly to the variable, via reflection or something like that?
By definition, a proxy is “a function authorized to act as the deputy or substitute for another”. This applies to Hibernate when we call Session. load() to create what is called an uninitialized proxy of our desired entity class. Simply put, Hibernate subclasses our entity class, using the CGLib library.
To enable lazy loading explicitly you must use “fetch = FetchType. LAZY” on an association that you want to lazy load when you are using hibernate annotations. @OneToMany( mappedBy = "category", fetch = FetchType.
How hibernate uses proxy pattern: Hibernate uses proxy pattern to lazy load entities. For lazy loaded entity or collection, hibernate defers database hit till any property other than identifier (Property marked with @Id) is accessed. The proxy object which hibernate creates has only identifier value set.
In eager loading strategy, if we load the User data, it will also load up all orders associated with it and will store it in a memory. But when we enable lazy loading, if we pull up a UserLazy, OrderDetail data won't be initialized and loaded into a memory until we make an explicit call to it.
First, two rules:
1) Suppose that you invoke a.equals(b)
where both a
and b
are proxies of the same entity. And lets say that equals
method is implemented like this:
public boolean equals(Object other) {
...
if (this.someField.equals(other.someField)) {
...
}
...
}
The equals
method of a
is delegated to the target instance forcing its full initialization. So you are safe regarding the fields in the a
instance (you can use them directly).
However, accessing the fields directly in the b
instance (other.someField
) is never valid. It does not matter whether b
is initialized or not, proxy instance is never initialized, only the target instance. So, someField
is always null
in the b
instance.
The correct implementation is to use getters at least for other
instance:
this.someField.equals(other.getSomeField())
or to be consistent:
this.getSomeField().equals(other.getSomeField())
Things are different when it comes to final
methods - Hibernate can't override them to delegate the call to the target. So, if the equals
method were final
in the previous example, you would get a NullPointerException
when accessing this.someField
.
All of this can be avoided by configuring Hibernate to use bytecode instrumentation instead of proxies, but that has its own pitfalls and is not adopted widely.
2) Again, it never initializes the proxy instance itself. When it comes to the target instance initialization, it depends whether field or property access is defined in the mappings. In both cases reflection is used (to assign values directly to fields in case of field access or to invoke setters in case of property access).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With