OneToOne lazy loading fails when fetch graph is involved

Description

Given:

  • Entity with a @OneToOne with a reverse @OneToOne mapping that uses @MapsId

  • Fetch graph that does not include the @OneToOne association

  • An entity instance persisted where the @OneToOne is not set.

When querying the entity with the fetch graph Hibernate creates a proxy for the association. When the proxy is accessed, the association does not evaluate to null as expected but a EntityNotFoundException is thrown.

Find reproducer attached.

.

Attachments

1

Activity

Show:

Marwan El Chamaa November 30, 2023 at 4:30 PM

We have been facing a similar issue after upgrading to Hibernate 6.2.13.Final (a newer version didn’t solve the issue either). I cannot paste any code, however, I’ll try to describe the issue generically as precise as possible. We have a one-to-one relationship between a parent and a child which uses as its id the id of the parent.

  • The parent's relationship to the child is annotated with @OneToOne(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL)

  • The child's relationship to the parent is annotated with @OneToOne, @MapsId and @JoinColumn(name = "PARENT_ID", referencedColumnName = "ID", nullable = false, foreignKey = @ForeignKey(name = "FK_CHILD_PARENT"))Not each parent has a child, such that in specific places in the code, i.e. on demand, we check if there's a child and proceed accordingly. This was working perfectly before the Hibernate upgrade.After the upgrade in one specific setting we have, upon their creation, the parents who don't have children were always trying to load the non-existent children, hence throwing an EntityNotFoundException instead of returning null if the child does not exist.

  • Method annotated with Spring's @Transactional (1) calls another not annotated method in which the parent is loaded and then (2) calls another method, which is also annotated with Spring's @Transactional, also in which the parent is loaded.

  • Loading the parent with a lazily fetched child in both cases, i.e. in a method within the same transaction and in another method in a new transaction merged into the original transaction, Hibernate tries to retrieve the child even when it doesn't exist and hence would throw an EntityNotFoundException.

  • If we load the parent directly in the first method and hence in the outer transaction, everything works as expected, as Hibernate seems to know from the information in the persistence context when the parent has a child and when it does not.We ended up solving the issue by writing code to always load the child after creating the parent and set it to null otherwise. Note that changing the fetching of the child to eager didn't help either.

We would still like to go back to using lazy loading without the above explained workaround.

Do you plan to fix this issue soon?

Many thanks in advance!

Fixed

Details

Assignee

Reporter

Worked in

Components

Sprint

Fix versions

Affects versions

Priority

Created July 20, 2023 at 2:54 PM
Updated February 2, 2024 at 8:53 AM
Resolved January 24, 2024 at 10:05 AM