I want to use Java record
s as embeddable objects with JPA. For example I want to wrap the ID in a record to make it typesafe:
@Entity public class DemoEntity { @EmbeddedId private Id id = new Id(UUID.randomUUID()); @Embeddable public static record Id(@Basic UUID value) implements Serializable {} }
But If I try to persist it with Hibernate 5.4.32 I get the following error:
org.hibernate.InstantiationException: No default constructor for entity: : com.example.demo.DemoEntity$Id at org.hibernate.tuple.PojoInstantiator.instantiate(PojoInstantiator.java:85) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final] at org.hibernate.tuple.component.AbstractComponentTuplizer.instantiate(AbstractComponentTuplizer.java:84) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final] ...
So it looks like Hibernate would treat the record Id
like an entity, although it is an @Embeddable
.
The same happens with non-id fields and @Embedded
:
@Embedded private Thing thing = new Thing("example"); @Embeddable public static record Thing(@Basic String value) implements Serializable {}
Is there a way to use @Embeddable record
s with JPA/Hibernate?
Advertisement
Answer
Java records
with a single field can be used for custom ID types or any other value object with AttributeConverter
s.
In the entity class the ID type is used with @Id
as usual:
@Entity public class DemoEntity { @Id private Id id = new Id(UUID.randomUUID()); public static record Id(UUID value) implements Serializable {} }
Note that the record Id
doesn’t have any annotation.
The converter makes it possible to use records:
@Converter(autoApply = true) public class DemoEntityIdConverter implements AttributeConverter<DemoEntity.Id, String> { @Override public String convertToDatabaseColumn(DemoEntity.Id id) { return id.value().toString(); } @Override public DemoEntity.Id convertToEntityAttribute(String s) { return new DemoEntity.Id(UUID.fromString(s)); } }
Don’t forget to set autoApply = true
to have this converter applied automatically (without referencing it explicitly on the respective field).
Records with more than one field could be mapped with a Hibernate UserType, but that is a bit cumbersome.