Hibernate 5.2 -> 5.3: Changed behaviour with lazy loading?


#1

After migration from Hibernate 5.2.4 to Hibernate 5.3.4 I have a problem with lazy loading.

I have a class A with a reference to an instance of class B.

It is annotated with:

  @OneToOne(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
  @JoinColumn(table = "TABLE_B", name = "A_B_ID")
  private B b;

  public B getB() {
return b;
}

I only set annotations on fields, not on getters.

When I load an instance of A, A.b is an instance of HibernateProxy. To replace the proxy I called a.getB().getIdentity(). (Hibernate 5.2). B.getIdentity() returns b.identity - this is annotated with @Id.

This triggered a call to the database and the HibernateProxy was replaced with an instance of B.

With Hibernate 5.3, this does not work. b.getIdentity() does not initalize b. But when I call another getter on B - B.getXYZ() - the replacement works?!

I annotate @Id on fields, not on methods. “Java Persistence with Hibernate”, 2nd edition, p. 285: “if @Id was on a field, then calling getId(), just like calling any other method, would initialize the proxy”. This seems to be changed with Hibernate 5.3?


#2

Check out the hibernate.jpa.compliance.proxy configuration property for more details about this behavior.

For performance reasons, you want Hibernate to work just like in 5.3 which is how it’s been for many years, even before 5.2. The “Java Persistence with Hibernate” might be wrong about that since it was first written in 2005 and probably that paragraph was not updated.

I don’t think that 5.2 behaved like how you describe because the JPA compliance mode is false by default.


#3

Thank you, Vlad!
The hibernate.jpa.compliance.proxy configuration property seems to be new with Hibernate 5.3, the 5.2 documentation doesn’t mention it.


#4

Maybe this bugfix changed the behaviour:
[HHH-11838]

It is also described here:
[HHH-3718]


#5

If you have a replicating test case, you should open a Jira issue for it.


#6

I think the change was intended (for the performance reasons as you explained).

I only use(d) it to get a fully initialized object in order to create a copy of an instance of “A” (via serialization/deserialization with XStream/Hibernate).