move custom id generators to Advanced + expand it

fix an ambiguity around "natural id" for Steve
This commit is contained in:
Gavin 2023-05-18 11:12:13 +02:00 committed by Christian Beikov
parent 674aff1161
commit a41805d3c2
2 changed files with 114 additions and 26 deletions

View File

@ -92,7 +92,7 @@ sessionFactory.inTransaction(session -> {
.setParameter("region", "es") .setParameter("region", "es")
.validate(); .validate();
... ...
}); });
---- ----
@ -268,6 +268,100 @@ When a value is generated by both inserts _and_ updates, use `@Generated(event={
For columns which should be generated using a SQL `generated always as` clause, prefer the `@GeneratedColumn` annotation, so that Hibernate automatically generates the correct DDL. For columns which should be generated using a SQL `generated always as` clause, prefer the `@GeneratedColumn` annotation, so that Hibernate automatically generates the correct DDL.
==== ====
Actually, the `@Generated` and `@GeneratedColumn` annotations are defined in terms of a more generic and user-extensible framework for handling attribute values generated in Java, or by the database.
[[user-defined-generators]]
=== User-defined generators
JPA doesn't define a standard way to extend the set of id generation strategies, but Hibernate does:
- the `Generator` hierarchy of interfaces in the package `org.hibernate.generator` lets you define new generators, and
- the `@IdGeneratorType` meta-annotation from the package `org.hibernate.annotations` lets you write an annotation which associates a `Generator` type with identifier attributes.
Furthermore, the `@ValueGenerationType` meta-annotation lets you write an annotation which associates a `Generator` type with a non-`@Id` attribute.
[NOTE]
// .The older APIs are still available in Hibernate 6
====
These APIs are new in Hibernate 6, and supersede the classic `IdentifierGenerator` interface and `@GenericGenerator` annotation from older versions of Hibernate.
However, the older APIs are still available and custom ``IdentifierGenerator``s written for older versions of Hibernate continue to work in Hibernate 6.
====
Hibernate has a range of built-in generators which are defined in terms of this new framework.
.Built-in generators
[cols="20,25,~"]
|===
| Annotation | Implementation | Purpose
| `@Generated` | `GeneratedGeneration` | Generically handles database-generated values
| `@GeneratedColumn` | `GeneratedAlwaysGeneration` | Handles values generated using `generated always`
| `@CurrentTimestamp` | `CurrentTimestampGeneration` | Generic support for database or in-memory generation of creation or update timestamps
| `@CreationTimestamp` | `CurrentTimestampGeneration` | A timestamp generated when an entity is first made persistent
| `@UpdateTimestamp` | `CurrentTimestampGeneration` | A timestamp generated when an entity is made persistent, and regenerated every time the entity is modified
| `@UuidGenerator` | `UuidGenerator` | A more flexible generator for RFC 4122 UUIDs
|===
Furthermore, support for JPA's standard id generation strategies is also defined in terms of this framework.
As an example, let's look at how `@UuidGenerator` is defined:
[source,java]
----
@IdGeneratorType(org.hibernate.id.uuid.UuidGenerator.class)
@ValueGenerationType(generatedBy = org.hibernate.id.uuid.UuidGenerator.class)
@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface UuidGenerator { ... }
----
`@UuidGenerator` is meta-annotated both `@IdGeneratorType` and `@ValueGenerationType` because it may be used to generate both ids and values of regular attributes.
Either way, this `Generator` class does the hard work:
[source,java]
----
public class UuidGenerator
// this generator produced values before SQL is executed
implements BeforeExecutionGenerator {
// constructors accept an instance of the @UuidGenerator
// annotation, allowing the generator to be "configured"
// called to create an id generator
public UuidGenerator(
org.hibernate.annotations.UuidGenerator config,
Member idMember,
CustomIdGeneratorCreationContext creationContext) {
this(config, idMember);
}
// called to create a generator for a regular attribute
public UuidGenerator(
org.hibernate.annotations.UuidGenerator config,
Member member,
GeneratorCreationContext creationContext) {
this(config, idMember);
}
...
@Override
public EnumSet<EventType> getEventTypes() {
// UUIDs are only assigned on insert, and never regenerated
return INSERT_ONLY;
}
@Override
public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) {
// actually generate a UUID and transform it to the required type
return valueTransformer.transform( generator.generateUuid( session ) );
}
}
----
You can find out more about custom generators from the Javadoc for `@IdGeneratorType` and for `org.hibernate.generator`.
[[naming-strategies]] [[naming-strategies]]
=== Naming strategies === Naming strategies

View File

@ -302,37 +302,29 @@ This fits somewhat uncomfortably with the common practice of annotating the `@Id
==== ====
As you can see, JPA provides quite adequate support for the most common strategies for system-generated ids. As you can see, JPA provides quite adequate support for the most common strategies for system-generated ids.
However, the annotations themselves are a little more intrusive than they should be, and there's no well-defined way to extend this framework to support custom strategies for id generation. However, the annotations themselves are a bit more intrusive than they should be, and there's no well-defined way to extend this framework to support custom strategies for id generation.
Nor may `@GeneratedValue` be used on a property not annotated `@Id`. Nor may `@GeneratedValue` be used on a property not annotated `@Id`.
Since custom id generation is a rather common requirement, Hibernate provides a very carefully-designed framework for user-defined ``Generator``s. Since custom id generation is a rather common requirement, Hibernate provides a very carefully-designed framework for user-defined ``Generator``s, which we'll discuss in <<user-defined-generators>>.
[[user-defined-generators]]
=== User-defined generators
JPA doesn't define a standard way to extend the set of id generation strategies, but Hibernate does:
- the `Generator` hierarchy of interfaces in the package `org.hibernate.generator` lets you define new generators, and
- the `@IdGeneratorType` meta-annotation from the package `org.hibernate.annotations` lets you write an annotation which associates a `Generator` type with identifier attributes.
Furthermore, the `@ValueGenerationType` meta-annotation lets you write an annotation which associates a `Generator` type with a non-`@Id` attribute.
[NOTE]
// .The older APIs are still available in Hibernate 6
====
These APIs are new in Hibernate 6, and supersede the classic `IdentifierGenerator` interface and `@GenericGenerator` annotation from older versions of Hibernate.
However, the older APIs are still available and custom ``IdentifierGenerator``s written for older versions of Hibernate continue to work in Hibernate 6.
====
You can find out more from the Javadoc for `@IdGeneratorType` and for `org.hibernate.generator`.
[[natural-identifiers]] [[natural-identifiers]]
=== Natural identifiers === Natural keys as identifiers
Not every id maps to a (system-generated) surrogate key. Not every identifier attribute maps to a (system-generated) surrogate key.
Primary keys which are meaningful to the user of the system are called _natural keys_. Primary keys which are meaningful to the user of the system are called _natural keys_.
When the primary key of a table is a natural key, we don't annotate the identifier attribute `@GeneratedValue`, and it's the responsibility of the application code to assign a value to the identifier attribute. When the primary key of a table is a natural key, we don't annotate the identifier attribute `@GeneratedValue`, and it's the responsibility of the application code to assign a value to the identifier attribute.
[source,java]
----
@Entity
class Book {
@Id
String isbn;
...
}
----
Of particular interest are natural keys which comprise more than one database column, and such natural keys are called _composite keys_. Of particular interest are natural keys which comprise more than one database column, and such natural keys are called _composite keys_.
[[composite-identifiers]] [[composite-identifiers]]
@ -477,8 +469,10 @@ And the `@OptimisticLock` annotation lets us selectively exclude certain fields
[[natural-id-attributes]] [[natural-id-attributes]]
=== Natural id attributes === Natural id attributes
Even when an entity has a surrogate key, it should still be possible to write down a combination of fields which uniquely identifies an instance of the entity, from the point of view of the user of the system. Even when an entity has a surrogate key, it should always be possible to write down a combination of fields which uniquely identifies an instance of the entity, from the point of view of the user of the system.
We call this combination of fields a _natural key_. This combination of fields is its natural key.
Above, we <<natural-identifiers,considered>> the case where the natural key coincides with the primary key.
Here, the natural key is a second unique key of the entity, distinct from its surrogate primary key.
[IMPORTANT] [IMPORTANT]
// .What if my entity has no natural key? // .What if my entity has no natural key?