From a41805d3c25b9d92130660cb1d7efa070195213c Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 18 May 2023 11:12:13 +0200 Subject: [PATCH] move custom id generators to Advanced + expand it fix an ambiguity around "natural id" for Steve --- .../main/asciidoc/introduction/Advanced.adoc | 96 ++++++++++++++++++- .../main/asciidoc/introduction/Entities.adoc | 44 ++++----- 2 files changed, 114 insertions(+), 26 deletions(-) diff --git a/documentation/src/main/asciidoc/introduction/Advanced.adoc b/documentation/src/main/asciidoc/introduction/Advanced.adoc index 4540bf67d8..73c3d9ff07 100644 --- a/documentation/src/main/asciidoc/introduction/Advanced.adoc +++ b/documentation/src/main/asciidoc/introduction/Advanced.adoc @@ -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 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 diff --git a/documentation/src/main/asciidoc/introduction/Entities.adoc b/documentation/src/main/asciidoc/introduction/Entities.adoc index 79ec367d56..cd8064fa92 100644 --- a/documentation/src/main/asciidoc/introduction/Entities.adoc +++ b/documentation/src/main/asciidoc/introduction/Entities.adoc @@ -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 <>. [[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 <> 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?