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
parent 97ab6f6879
commit bb1f5bf8e3
2 changed files with 114 additions and 26 deletions

View File

@ -92,7 +92,7 @@ sessionFactory.inTransaction(session -> {
.setParameter("region", "es")
.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.
====
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

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.
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`.
Since custom id generation is a rather common requirement, Hibernate provides a very carefully-designed framework for user-defined ``Generator``s.
[[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`.
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>>.
[[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_.
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_.
[[composite-identifiers]]
@ -477,8 +469,10 @@ And the `@OptimisticLock` annotation lets us selectively exclude certain fields
[[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.
We call this combination of fields a _natural key_.
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.
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]
// .What if my entity has no natural key?