HHH-16143 Documentation for composite aggregates
This commit is contained in:
parent
a66d1a5ec3
commit
7a55e332ed
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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[]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue