Option for injecting empty (non-null) embedded when all columns are NULL

Description

When all of the values in an @Embedded object are NULL, Hibernate sets the field in the parent object to null. This can lead to NullPointerExceptions if not handled correctly.

There are two ways to handle this:

  • Make sure code that calls the @Embedded getter handles null return values.

  • Add a non-nullable field to the embedded object, thus avoiding the behavior entirely.

I suggest to make this optional. I.e. add an annotation which would make Hibernate create an object with it's properties set to null.

In my oppinion, this would improve usability and user friendliness.
Having an embedded object with all props potentially set to null is perfectly valid use case - e.g. an object with optional traits grouped in @Embeddable class; When this is created, the traits are initially empty.

This was reported years ago as a bug, but at that time, it was closed as Won't fix for unknown reason.

Environment

JBoss EAP 6.0.1.ER1

Activity

Show:
Pavel Horal
July 19, 2013, 12:25 PM
Edited

Unfortunately I am not able to persuade Hibernate to use field access for my embedded object (moving @Embedded to field or adding @AccessType doesn't help). But using common sense it can not work with that.

Also I have a small modification to the workaround - we will use a bit more semantically correct alternative (instead of ignoring null value, we will interpret it as an "empty object"):

Pavel Horal
August 7, 2013, 9:21 AM
Edited

Not sure what have changed since I first tested the workaround, but we have tried to actually implement it in our application and Hibernate again handles everything as "dirty". Loaded state is always "null", new state is always empty entity. So there must be something else to make this workaround work. It does not work as is.

UPDATE: The reason for the non-working workaround is ComponentType#hydrate, which returns null if all its properties are null. Thus it is not possible to have non-null loaded entity state.

Markos Fragkakis
October 21, 2013, 10:06 AM

I am getting the same with Pavel, debugged all the way to ComponentType#hydrate.

Steve Ebersole
October 21, 2013, 2:42 PM

You would need to add handling to the actual reading of a composite out of the ResultSet to handle all tuples as null to mean "create an empty composite instance". That's how "loaded state" is populated. This would require changing org.hibernate.type.ComponentType#resolve from:

ComponentType.java

to something like:

ComponentType.java

You'd also need to make certain that this is accounted for in the various equality checking methods (isSame, isEqual, compare, isModified, etc).

Also this will require lots of testing as this is a long standing assumption in Hibernate code.

Ultimately this is very low on my todo list. But if someone wants to work on it and work up a Pull Request, that obviously raises the likelihood that it gets resolved sooner. I laid out the main changes above. Just make sure that createEmptyCompositesEnabled():

  • delegates to SessionFactory/Settings

  • is fueled by a user-controlled setting

  • is disabled by default (opt-in behavior)

EricB
July 8, 2014, 6:14 PM
Edited

This is probably going to be frowned upon by purists, but until there is a way to configure hibernate to ensure @Embedded fields are non-null, I've written an AspectJ to populate the value if null.

This isn't fully tested, and definitely not tested against Collections of embedded objects, so buyer-beware.

Fixed

Assignee

Guillaume Smet

Reporter

Ondra Žižka

Fix versions

Labels

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Affects versions

Priority

Major
Configure