HHH-16143 Documentation for composite aggregates

This commit is contained in:
Christian Beikov 2023-02-06 18:00:01 +01:00
parent a66d1a5ec3
commit 7a55e332ed
3 changed files with 174 additions and 4 deletions

View File

@ -442,3 +442,173 @@ include::{extrasdir}/embeddable/embeddable-multiple-namingstrategy-entity-mappin
----
You could even develop your own naming strategy to do other types of implicit naming strategies.
[[embeddable-mapping-aggregate]]
==== Aggregate embeddable mapping
An embeddable mapping is usually just a way to encapsulate columns of a table into a Java type,
but as of Hibernate 6.2, it is also possible to map embeddable types as SQL aggregate types.
Currently, there are three possible SQL aggregate types which can be specified by annotating one of the following
annotations on a persistent attribute:
* `@Struct` - maps to a named SQL object type
* `@JdbcTypeCode(SqlTypes.JSON)` - maps to the SQL type JSON
* `@JdbcTypeCode(SqlTypes.SQLXML)` - maps to the SQL type XML
Any read or assignment (in an update statement) expression for an attribute of such an embeddable
will resolve to the proper SQL expression to access/update the attribute of the SQL type.
Since object, JSON and XML types are not supported equally on all databases, beware that not every mapping will work on all databases.
The following table outlines the current support for the different aggregate types:
|===
|Database |Struct |JSON |XML
|PostgreSQL
|Yes
|Yes
|No (not yet)
|Oracle
|Yes
|Yes
|No (not yet)
|DB2
|Yes
|No (not yet)
|No (not yet)
|SQL Server
|No (not yet)
|No (not yet)
|No (not yet)
|===
Also note that embeddable types that are used in aggregate mappings do not yet support all kinds of attribute mappings, most notably:
* Association mappings (`@ManyToOne`, `@OneToOne`, `@OneToMany`, `@ManyToMany`, `@ElementCollection`)
* Basic array mappings
===== `@Struct` aggregate embeddable mapping
The `@Struct` annotation can be placed on either the persistent attribute, or the embeddable type,
and requires the specification of a name i.e. the name of the SQL object type that it maps to.
The following example mapping, maps the `EmbeddableAggregate` type to the SQL object type `structType`:
.Mapping embeddable as SQL object type on persistent attribute level
====
[source,java]
----
include::{example-dir-emeddable}/StructEmbeddableTest.java[tag=embeddable-struct-type-mapping-example, indent=0]
----
====
The schema generation will by default emit DDL for that object type, which looks something along the lines of
====
[source,sql]
----
create type structType as (
...
)
create table StructHolder as (
id bigint not null primary key,
aggregate structType
)
----
====
The name and the nullability of the column can be refined through applying a `@Column` on the persistent attribute.
One very important thing to note is that the order of columns in the DDL definition of a type must match the order that Hibernate expects.
By default, the order of columns is based on the alphabetical ordering of the embeddable type attribute names.
Consider the following class:
====
[source,java]
----
@Embeddable
@Struct(name = "myStruct")
public class MyStruct {
@Column(name = "b")
String attr1;
@Column(name = "a")
String attr2;
}
----
====
The expected ordering of columns will be `(b,a)`, because the name `attr1` comes before `attr2` in alphabetical ordering.
This example aims at showing the importance of the persistent attribute name.
Defining the embeddable type as Java record instead of a class can force a particular ordering through the definition of canonical constructor.
====
[source,java]
----
@Embeddable
@Struct(name = "myStruct")
public record MyStruct (
@Column(name = "a")
String attr2,
@Column(name = "b")
String attr1
) {}
----
====
In this particular example, the expected ordering of columns will be `(a,b)`, because the canonical constructor of the record
defines a specific ordering of persistent attributes, which Hibernate makes use of for `@Struct` mappings.
It is not necessary to switch to Java records to configure the order though.
The `@Struct` annotation allows specifying the order through the `attributes` member,
an array of attribute names that the embeddable type declares, which defines the order in columns appear in the SQL object type.
The same ordering as with the Java record can be achieved this way:
====
[source,java]
----
@Embeddable
@Struct(name = "myStruct", attributes = {"attr2", "attr1"})
public class MyStruct {
@Column(name = "b")
String attr1;
@Column(name = "a")
String attr2;
}
----
====
===== JSON/XML aggregate embeddable mapping
The `@JdbcTypeCode` annotation for JSON and XML mappings can only be placed on the persistent attribute.
The following example mapping, maps the `EmbeddableAggregate` type to the JSON SQL type:
.Mapping embeddable as JSON
====
[source,java]
----
include::{example-dir-emeddable}/JsonEmbeddableTest.java[tag=embeddable-json-type-mapping-example, indent=0]
----
====
The schema generation will by default emit DDL that ensures the constraints of the embeddable type are respected, which looks something along the lines of
====
[source,sql]
----
create table JsonHolder as (
id bigint not null primary key,
aggregate json,
check (json_value(aggregate, '$.attribute1') is not null)
)
----
====
Again, the name and the nullability of the `aggregate` column can be refined through applying a `@Column` on the persistent attribute.

View File

@ -302,8 +302,8 @@ public class JsonEmbeddableTest extends BaseSessionFactoryFunctionalTest {
@JdbcTypeCode(SqlTypes.JSON)
private EmbeddableAggregate aggregate;
//end::embeddable-json-type-mapping-example[]
//Getters and setters are omitted for brevity
//end::embeddable-json-type-mapping-example[]
public JsonHolder() {
}

View File

@ -555,13 +555,11 @@ public class StructEmbeddableTest extends BaseSessionFactoryFunctionalTest {
@Id
private Long id;
//end::embeddable-struct-type-mapping-example[]
@Struct(name = "structType")
//tag::embeddable-struct-type-mapping-example[]
private EmbeddableAggregate aggregate;
//end::embeddable-struct-type-mapping-example[]
//Getters and setters are omitted for brevity
//end::embeddable-struct-type-mapping-example[]
public StructHolder() {
}
@ -587,5 +585,7 @@ public class StructEmbeddableTest extends BaseSessionFactoryFunctionalTest {
this.aggregate = aggregate;
}
//tag::embeddable-struct-type-mapping-example[]
}
//end::embeddable-struct-type-mapping-example[]
}