Hibernation: Proxy Implementation Details (Lazy Fetch)

I found out that Hibernate does not return an instance of your actual entity class when it gives the query result, but instead returns a proxy instance that is dynamically subclassed from your actual entity class. I understand the reason for this behavior, since it allows you to implement lazy initialization. However, I still have a few questions left unanswered about the implementation details of these proxy classes:

  • Will the loaded ribbon field load ONLY when I use the getter? What if I use a field, say, in the equals or hashCode method? Will the execution of these methods result in a NullPointerException if I have not yet called getter of this field?

  • How exactly does Hibernate initialize a field when it is initialized? Does it follow the field definition method that I defined in the entity class, or will it assign the value directly to the variable through reflection or something similar?

+7
fetch proxy lazy-evaluation hibernate
source share
1 answer

Firstly, two rules:

  • The proxy server delegates all non-final method calls to the target instance, with the exception of the method to get the identifier if the mappings specify access to the resource for id.
  • Proxy objects are never initialized; the target instance is initialized.

1) Suppose you call a.equals(b) , where both a and b are proxies of the same object. And let's say that the equals method is implemented as follows:

 public boolean equals(Object other) { ... if (this.someField.equals(other.someField)) { ... } ... } 

The equals a method is delegated to the target instance, causing it to be fully initialized. Thus, you are not safe with the fields in instance a (you can use them directly).

However, accessing fields directly in instance b ( other.someField ) never matches. It does not matter if b initialized or not, the proxy instance is never initialized, but only the target instance. So, someField always null in instance b .

The correct implementation is to use getters for at least the other instance:

 this.someField.equals(other.getSomeField()) 

or be consistent:

 this.getSomeField().equals(other.getSomeField()) 

In final methods, everything is different: Hibernate cannot override them to delegate a call to the target. So, if the equals method was final in the previous example, you will get a NullPointerException when accessing this.someField .

All this can be avoided by configuring Hibernate to use bytecode tools instead of proxies, but it has its own errors and is not widely accepted.

2) Again, it never initializes the proxy instance itself. When it comes to initializing the target instance, it depends on whether the field or access to properties in the mappings is determined. In both cases, reflection is used (to assign values ​​directly to fields in case of access to the field or to call setters in case of access to properties).

+1
source share

All Articles