Removing an entity with a bidirectional one-to-one relation causes an error

Environment

  • Operative system: Debian GNU/Linu 13.5 (Trixie)
  • Java runtime: OpenJDK 21.0.11
  • Database engine: PostgreSQL 17.10
  • Application server: Wildfly 40.0.1
  • Persistence provider: Hibernate ORM 7.4.1

Summary

I’ve 2 entity beans, A and B, that keep a bidirectional one-to-one relation between them. B
is the owning side of that relation.

When I follow these steps:

  1. I load B (using EntityManager.find()).
  2. I obtain A through an accessor in B and read any of its fields.
  3. I remove B (using EntityManager.remove()).

Then Hibernate complains that the other side of the relation (A) references an unsaved transient
instance (B) and that I should persist it first. But I’m trying to delete such instance (B), so
obviously that’s not an option.

In version 6.4 of Hibernate and previous ones it worked fine, but since version 6.6 onward, it
doesn’t anymore. Why is it? Is it a bug?

Example

I’ve tested it with 2 minimal entity beans: Person and AccessCard. AccessCard is the owning
side and the instance I’m trying to remove.

In Wildfly 40.0.1 (from versions 34 to 41-beta really), I get an error message like this:

ARJUNA012125: TwoPhaseCoordinator.beforeCompletion - failed for SynchronizationImple<
0:ffff7f000101:5cf8ae27:6a4f57de:742,
org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization@2ef150c3 >:
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Persistent instance
of 'com.example.Person' references an unsaved transient instance of 'com.example.AccessCard'
(persist the transient instance before flushing) [com.example.Person.accessCard ->
com.example.AccessCard]

Workarounds.

I’ve found out that by detaching the non-owning side BEFORE removing the entity in question (using EntityManager.detach()) works fine. But it looks ugly to me.

Other option is to remove the bidirectional relation… But I prefer to keep it as is, because
to me that’s the correct model in my applications.

Thank you!

This is on purpose. You have to remove set Person#accessCard to null i.e. manage both sides of the relationship. Failing to do so can lead to various problems/inconsistencies, which is why we added this validation.

@beikov Thank you for your explanation!

In my current model, in the non-owning side, I only have a getter but not a setter. It’s my way to express that this type of field is read-only and force myself to set the relation from the owning side. I carry this idea from the relations of type one-to-many, in which I do exactly the same. I don’t remember why, but someday I decided that fields representing one-to-many relations gave me less problems by treating them as read-only (the fields, not the collections their selves). Maybe I faced some problem in early versions of Hibernate and that was my way to avoid it.

Anyway, I’m going to change my model so that the one-to-one relations can be set from both sides. And with regard to the one-to-many relations, I’ll reconsider my current design.