discuss mapping embeddables to UDTs or JSON

This commit is contained in:
Gavin 2023-05-15 00:27:06 +02:00
parent c76a36a1f2
commit a4d9c85ba3
2 changed files with 90 additions and 5 deletions

View File

@ -792,7 +792,7 @@ Let's abandon our analogy right here, before we start calling this basic type a
[[embeddable-objects]]
=== Embeddable objects
An embeddable object is a Java class whose state maps to multiple columns of a table, but which doesn't itself have a persistent identity.
An embeddable object is a Java class whose state maps to multiple columns of a table, but which doesn't have its own persistent identity.
That is, it's a class with mapped attributes, but no `@Id` attribute.
An embeddable object can only be made persistent by assigning it to the attribute of an entity.
@ -828,7 +828,7 @@ class Name {
An embeddable class must satisfy the same requirements that entity classes satisfy, with the exception that an embeddable class has no `@Id` attribute.
In particular, it must have a constructor with no parameters.
Alternatively, and embeddable type may be defined as a Java record type:
Alternatively, an embeddable type may be defined as a Java record type:
[source,java]
----
@ -855,6 +855,9 @@ class Author {
}
----
Embeddable types can be nested.
That is, an `@Embeddable` class may have an attribute whose type is itself a different `@Embeddable` class.
[TIP]
// .The `@Embedded` annotation is not required
====
@ -862,12 +865,16 @@ JPA provides an `@Embedded` annotation to identify an attribute of an entity tha
This annotation is completely optional, and so we don't usually use it.
====
Usually, embeddable types are stored in a "flattened" format.
Their attributes map columns of the table of their parent entity.
Later, in <<mapping-embeddables>>, we'll see a couple of different options.
An attribute of embeddable type represents a relationship between a Java object with a persistent identity, and a Java object with no persistent identity.
You can think of it as a whole-part relationship.
We can think of it as a whole/part relationship.
The embeddable object belongs to the entity, and can't be shared with other entity instances.
And it exits for only as long as its parent entity exists.
Now we'll discuss a different kind of relationship: a relationship between Java objects that each have their persistent identity and persistence lifecycle.
Next we'll discuss a different kind of relationship: a relationship between Java objects that each have their persistent identity and persistence lifecycle.
[[associations]]
=== Associations

View File

@ -653,6 +653,84 @@ InputStream bytes = book.images.getBinaryStream();
Of course, the behavior here depends very much on the JDBC driver, and so we really can't promise that this is a sensible thing to do on your database.
[[mapping-embeddables]]
=== Mapping embeddable types to UDTs or to JSON
There's a couple of alternative ways to represent an embeddable type on the database side.
[discrete]
==== Embeddables as UDTs
First, a really nice option, at least in the case of Java record types, and for databases which support _user-defined types_ (UDTs), is to define a UDT which represents the record type.
Hibernate 6 makes this really easy.
Just annotate the record type, or the attribute which holds a reference to it, with the new `@Struct` annotation:
[source,java]
----
@Embeddable
@Struct(name="PersonName")
record Name(String firstName, String middleName, String lastName) {}
----
[source,java]
----
@Entity
class Person {
...
Name name;
...
}
----
This results in the following UDT:
[source,sql]
----
create type PersonName as (firstName varchar(255), middleName varchar(255), lastName varchar(255))
----
And the `name` column of the `Author` table will have the type `PersonName`.
[discrete]
==== Embeddables to JSON
A second option that's available is to map the embeddable type to a `JSON` (or `JSONB`) column.
Now, this isn't something we would exactly _recommend_ if you're defining a data model from scratch, but it's at least useful for mapping pre-existing tables with JSON-typed columns.
Since embeddable types are nestable, we can map some JSON formats this way, and even query JSON properties using HQL.
[NOTE]
====
At this time, JSON arrays are not supported!
====
To map an attribute of embeddable type to JSON, we must annotate the attribute `@JdbcTypeCode(SqlTypes.JSON)`, instead of annotating the embeddable type.
But the embeddable type `Name` should still be annotated `@Embeddable` if we want to query its attributes using HQL.
[source,java]
----
@Embeddable
record Name(String firstName, String middleName, String lastName) {}
----
[source,java]
----
@Entity
class Person {
...
@JdbcTypeCode(SqlTypes.JSON)
Name name;
...
}
----
We also need to add Jackson or an implementation of JSONB—for example, Yasson—to our runtime classpath.
To use Jackson we could add this line to our Gradle build:
[source,groovy]
----
runtimeOnly 'com.fasterxml.jackson.core:jackson-databind:{jacksonVersion}'
----
Now the `name` column of the `Author` table will have the type `jsonb`, and Hibernate will automatically use Jackson to serialize a `Name` to and from JSON format.
[[mapping-formulas]]
=== Mapping to formulas