Document @IdGeneratorType;

Some work to clean up User Guide sections on id-generators
This commit is contained in:
Steve Ebersole 2022-01-10 09:54:06 -06:00
parent 64d25f16d1
commit d2584730f3
8 changed files with 310 additions and 138 deletions

View File

@ -1,7 +1,10 @@
[[identifiers]]
=== Identifiers
:projectRootDir: ../../../../../../..
:sourcedir: ../../../../../test/java/org/hibernate/userguide/mapping/identifier
:sourcedir-associations: ../../../../../test/java/org/hibernate/userguide/associations
:coreTestsDir: {projectRootDir}/hibernate-core/src/test/java
:jpaJavadocUrl: https://javadoc.io/doc/jakarta.persistence/jakarta.persistence-api/latest/jakarta.persistence
:extrasdir: extras
Identifiers model the primary key of an entity. They are used to uniquely identify each specific entity.
@ -11,11 +14,9 @@ Hibernate and Jakarta Persistence both make the following assumptions about the
`UNIQUE`:: The values must uniquely identify each row.
`NOT NULL`:: The values cannot be null. For composite ids, no part can
be null.
`IMMUTABLE`:: The values, once inserted, can never be changed.
This is more a general guide, than a hard-fast rule as opinions vary.
Jakarta Persistence defines the behavior of changing the value of the identifier attribute to be undefined; Hibernate simply does not support that.
In cases where the values for the PK you have chosen will be updated, Hibernate recommends mapping the mutable value as a natural id, and use a surrogate id for the PK.
See <<chapters/domain/natural_id.adoc#naturalid,Natural Ids>>.
`IMMUTABLE`:: The values, once inserted, can never be changed. In cases where the values for the PK you have
chosen will be updated, Hibernate recommends mapping the mutable value as a natural id, and use a surrogate id
for the PK. See <<chapters/domain/natural_id.adoc#naturalid,Natural Ids>>.
[NOTE]
====
@ -26,30 +27,31 @@ However, this documentation will continue to use the terms identifier and primar
Every entity must define an identifier. For entity inheritance hierarchies, the identifier must be defined just on the entity that is the root of the hierarchy.
An identifier might be simple (single value) or composite (multiple values).
An identifier may be <<identifiers-simple,simple>> or <<identifiers-composite,composite>>.
[[identifiers-simple]]
==== Simple identifiers
Simple identifiers map to a single basic attribute, and are denoted using the `jakarta.persistence.Id` annotation.
According to Jakarta Persistence only the following types should be used as identifier attribute types:
According to Jakarta Persistence, only the following types are portably supported for use as identifier attribute types:
* any Java primitive type
* any primitive wrapper type
* `java.lang.String`
* `java.util.Date` (TemporalType#DATE)
* `java.util.Date` (`TemporalType#DATE`)
* `java.sql.Date`
* `java.math.BigDecimal`
* `java.math.BigInteger`
Any types used for identifier attributes beyond this list will not be portable.
Hibernate, however, supports a more broad set of types to be used for identifiers (`UUID`, e.g.).
[[identifiers-simple-assigned]]
===== Assigned identifiers
Values for simple identifiers can be assigned, as we have seen in the examples above.
The expectation for assigned identifier values is that the application assigns (sets them on the entity attribute) prior to calling save/persist.
Values for simple identifiers can be assigned, which simply means that the application itself will
assign the value to the identifier attribute prior to persisting the entity.
[[identifiers-simple-assigned-mapping-example]]
.Simple assigned entity identifier
@ -60,10 +62,12 @@ include::{sourcedir}/AssignedIdentifierTest.java[tag=identifiers-simple-assigned
----
====
[[identifiers-simple-generated]]
===== Generated identifiers
Values for simple identifiers can be generated. To denote that an identifier attribute is generated, it is annotated with `jakarta.persistence.GeneratedValue`
Values for simple identifiers can be generated. To denote that an identifier attribute is generated, it is
annotated with `jakarta.persistence.GeneratedValue`
[[identifiers-simple-generated-mapping-example]]
.Simple generated identifier
@ -74,11 +78,16 @@ include::{sourcedir}/GeneratedIdentifierTest.java[tag=identifiers-simple-generat
----
====
Additionally, to the type restriction list above, Jakarta Persistence says that if using generated identifier values (see below) only integer types (short, int, long) will be portably supported.
When an entity with an identifier defined as generated is persisted, Hibernate will generate the value based on
an associated generation strategy. Identifier value generations strategies are discussed in detail in the
<<identifiers-generators>> section.
The expectation for generated identifier values is that Hibernate will generate the value when the save/persist occurs.
[NOTE]
====
While Hibernate supports almost any valid basic type be used for generated identifier values,
Jakarta Persistence restricts the allowable types to just integer types.
====
Identifier value generations strategies are discussed in detail in the <<identifiers-generators>> section.
[[identifiers-composite]]
==== Composite identifiers
@ -95,19 +104,22 @@ or defined using the `jakarta.persistence.IdClass` annotation (see <<identifiers
[NOTE]
====
The restriction that a composite identifier has to be represented by a "primary key class" (e.g. `@EmbeddedId` or `@IdClass`) is only Jakarta Persistence-specific.
The restriction that a composite identifier has to be represented by a "primary key class" (e.g. `@EmbeddedId` or `@IdClass`) is only
Jakarta Persistence-specific.
Hibernate does allow composite identifiers to be defined without a "primary key class" via multiple `@Id` attributes.
Hibernate does allow composite identifiers to be defined without a "primary key class" via multiple `@Id` attributes, although that
is generally considered poor design.
====
The attributes making up the composition can be either basic, composite, `@ManyToOne`.
Note especially that collection and one-to-one are never appropriate.
The attributes making up the composition can be either basic, composite or `@ManyToOne`. Note especially that collection and one-to-one
are never appropriate.
[[identifiers-composite-aggregated]]
==== Composite identifiers with `@EmbeddedId`
Modeling a composite identifier using an EmbeddedId simply means defining an embeddable to be a composition for the one or more attributes making up the identifier,
and then exposing an attribute of that embeddable type on the entity.
Modeling a composite identifier using an EmbeddedId simply means defining an embeddable to be a composition for the attributes making up
the identifier, and then exposing an attribute of that embeddable type on the entity.
[[identifiers-basic-embeddedid-mapping-example]]
.Basic `@EmbeddedId`
@ -140,8 +152,8 @@ In Jakarta Persistence terms, one would use "derived identifiers". For more deta
[[identifiers-composite-nonaggregated]]
==== Composite identifiers with `@IdClass`
Modeling a composite identifier using an IdClass differs from using an EmbeddedId in that the entity defines each individual attribute making up the composition.
The IdClass simply acts as a "shadow".
Modeling a composite identifier using an IdClass differs from using an EmbeddedId in that the entity defines each individual
attribute making up the composition. The IdClass is used as the representation of the identifier for load-by-id operations.
[[identifiers-basic-idclass-mapping-example]]
.Basic `@IdClass`
@ -152,7 +164,7 @@ include::{sourcedir}/IdClassTest.java[tag=identifiers-basic-idclass-mapping-exam
----
====
Non-aggregated composite identifiers can also contain ManyToOne attributes as we saw with aggregated ones (still non-portably).
Non-aggregated composite identifiers can also contain ManyToOne attributes as we saw with aggregated mappings, though still non-portably.
[[identifiers-basic-idclass-manytoone-mapping-example]]
.IdClass with `@ManyToOne`
@ -176,10 +188,11 @@ include::{sourcedir}/IdClassGeneratedValueTest.java[tag=identifiers-basic-idclas
[NOTE]
====
This feature which allows auto-generated values in composite identifiers exists because of a highly questionable interpretation of the Jakarta Persistence specification made by the SpecJ committee.
This feature which allows auto-generated values in composite identifiers exists because of a highly questionable interpretation
of the Jakarta Persistence specification made by the SpecJ committee.
Hibernate does not feel that Jakarta Persistence defines support for this, but added the feature simply to be usable in SpecJ benchmarks.
Use of this feature may or may not be portable from a Jakarta Persistence perspective.
Hibernate does not feel that Jakarta Persistence defines support for this, but added the feature simply to be usable in SpecJ
benchmarks. Use of this feature may or may not be portable from a Jakarta Persistence perspective.
====
[[identifiers-composite-associations]]
@ -272,32 +285,132 @@ include::{sourcedir}/composite/EmbeddedIdDatabaseGeneratedValueTest.java[tag=ide
Notice that the `createdOn` property of the `EventId` composite identifier was generated by calling the `CURRENT_TIMESTAMP` database function,
and we assigned it to the composite identifier prior to persisting the `Event` entity.
[[identifiers-generators]]
==== Generated identifier values
Hibernate supports identifier value generation across a number of different types.
Remember that Jakarta Persistence portably defines identifier value generation just for integer types.
[NOTE]
====
You can also auto-generate values for non-identifier attributes. For more details, see the <<chapters/domain/basic_types.adoc#mapping-generated,Generated properties>> section.
====
Hibernate supports identifier value generation across a number of different types.
Remember that Jakarta Persistence portably defines identifier value generation just for integer types.
Identifier value generation is indicated using the `jakarta.persistence.GeneratedValue` annotation.
The most important piece of information here is the specified `jakarta.persistence.GenerationType` which indicates how values will be generated.
`AUTO` (the default):: Indicates that the persistence provider (Hibernate) should choose an appropriate generation strategy. See <<identifiers-generators-auto>>.
`IDENTITY`:: Indicates that database IDENTITY columns will be used for primary key value generation. See <<identifiers-generators-identity>>.
`SEQUENCE`:: Indicates that database sequence should be used for obtaining primary key values. See <<identifiers-generators-sequence>>.
`TABLE`:: Indicates that a database table should be used for obtaining primary key values. See <<identifiers-generators-table>>.
[[identifiers-generators-auto]]
==== Interpreting AUTO
How a persistence provider interprets the AUTO generation type is left up to the provider.
The default behavior is to look at the Java type of the identifier attribute, plus what the underlying database supports.
If the identifier type is UUID, Hibernate is going to use a <<identifiers-generators-uuid, UUID identifier>>.
If the identifier type is numeric (e.g. `Long`, `Integer`), then Hibernate will use its `SequenceStyleGenerator` which
resolves to a SEQUENCE generation if the underlying database supports sequences and a table-based generation otherwise.
[[identifiers-generators-sequence]]
==== Using sequences
For implementing database sequence-based identifier value generation Hibernate makes use of its
`org.hibernate.id.enhanced.SequenceStyleGenerator` id generator. It is important to note that `SequenceStyleGenerator`
is capable of working against databases that do not support sequences by transparently switching to a table as the
underlying backing, which gives Hibernate a huge degree of portability across databases while still maintaining consistent
id generation behavior (versus say choosing between SEQUENCE and IDENTITY).
[[identifiers-generators-sequence-unnamed]]
.Implicit sequence
====
[source,java]
----
include::{sourcedir}/SequenceGeneratorUnnamedTest.java[tag=identifiers-generators-sequence-mapping-example, indent=0]
----
====
Notice that the mapping does not specify the name of the sequence to use. In such cases, Hibernate will assume a
sequence name based on the name of the table to which the entity is mapped. Here, since the entity is mapped to
a table named `product`, Hibernate will use a sequence named `product_seq`.
[NOTE]
====
The discussions below assume that the application is using Hibernate's "new generator mappings" as indicated by the `hibernate.id.new_generator_mappings` setting or
`MetadataBuilder.enableNewIdentifierGeneratorSupport` method during bootstrap.
Starting with Hibernate 5, this is set to `true` by default.
In applications where the `hibernate.id.new_generator_mappings` configuration is set to `false` the resolutions discussed here will be very different.
The rest of the discussion here assumes this setting is enabled (`true`).
When using `@Subselect` mappings, using the "table name" is not valid so Hibernate falls back to using the entity
name as the base along with the `_seq` suffix.
====
To specify the sequence name explicitly, the simplest form is to specify `@GeneratedValue#generator`.
[[identifiers-generators-sequence-named]]
.Named sequence
====
[source,java]
----
include::{sourcedir}/SequenceGeneratorAnnotationNameTest.java[tag=identifiers-generators-sequence-mapping-example, indent=0]
----
====
For this mapping, Hibernate will use `explicit_product_sequence` as the name of the sequence.
For more advanced configuration, Jakarta Persistence defines the `@SequenceGenerator` annotation.
[[identifiers-generators-sequence-gen-simple]]
.Simple @SequenceGenerator
====
[source,java]
----
include::{sourcedir}/SequenceGeneratorNamedTest.java[tag=identifiers-generators-sequence-mapping-example, indent=0]
----
====
This is simply a more verbose form of the mapping in <<identifiers-generators-sequence-named>>.
However, the `jakarta.persistence.SequenceGenerator` annotation allows you to specify additional
configurations as well.
[[identifiers-generators-sequence-configured]]
.Sequence configuration
====
[source,java]
----
include::{sourcedir}/SequenceGeneratorConfiguredTest.java[tag=identifiers-generators-sequence-mapping-example, indent=0]
----
====
Again the mapping specifies `explicit_product_sequence` as the physical sequence name, but it also specifies an
explicit allocation-size ("increment by").
[[identifiers-generators-identity]]
==== Using IDENTITY columns
For implementing identifier value generation based on IDENTITY columns,
Hibernate makes use of its `org.hibernate.id.IdentityGenerator` id generator which expects the identifier to be generated by INSERT into the table.
IdentityGenerator understands 3 different ways that the INSERT-generated value might be retrieved:
* If Hibernate believes the JDBC environment supports `java.sql.Statement#getGeneratedKeys`, then that approach will be used for extracting the IDENTITY generated keys.
* Otherwise, if `Dialect#supportsInsertSelectIdentity` reports true, Hibernate will use the Dialect specific INSERT+SELECT statement syntax.
* Otherwise, Hibernate will expect that the database supports some form of asking for the most recently inserted IDENTITY value via a separate SQL command as indicated by `Dialect#getIdentitySelectString`.
[IMPORTANT]
====
It is important to realize that using IDENTITY columns imposes a runtime behavior where the entity
row *must* be physically inserted prior to the identifier value being known.
This can mess up extended persistence contexts (long conversations).
Because of the runtime imposition/inconsistency, Hibernate suggests other forms of identifier value generation
be used (e.g. SEQUENCE) with extended contexts.
In Hibernate 5.3, Hibernate attempts to delay the insert of entities if the flush-mode does not equal `AUTO`.
This was slightly problematic for entities that used `IDENTITY` or `SEQUENCE` generated identifiers that were
also involved in some form of association with another entity in the same transaction.
@ -315,92 +428,6 @@ behavior across Hibernate 5.x releases. If this configuration setting is necess
a JIRA and report the mapping so that the algorithm can be reviewed.
====
`AUTO` (the default):: Indicates that the persistence provider (Hibernate) should choose an appropriate generation strategy. See <<identifiers-generators-auto>>.
`IDENTITY`:: Indicates that database IDENTITY columns will be used for primary key value generation. See <<identifiers-generators-identity>>.
`SEQUENCE`:: Indicates that database sequence should be used for obtaining primary key values. See <<identifiers-generators-sequence>>.
`TABLE`:: Indicates that a database table should be used for obtaining primary key values. See <<identifiers-generators-table>>.
[[identifiers-generators-auto]]
==== Interpreting AUTO
How a persistence provider interprets the AUTO generation type is left up to the provider.
The default behavior is to look at the Java type of the identifier attribute.
If the identifier type is UUID, Hibernate is going to use a <<identifiers-generators-uuid, UUID identifier>>.
If the identifier type is numerical (e.g. `Long`, `Integer`), then Hibernate is going to use the `IdGeneratorStrategyInterpreter` to resolve the identifier generator strategy.
The `IdGeneratorStrategyInterpreter` has two implementations:
`FallbackInterpreter`::
This is the default strategy since Hibernate 5.0. For older versions, this strategy is enabled through the <<appendices/Configurations.adoc#configurations-mapping,`hibernate.id.new_generator_mappings`>> configuration property.
When using this strategy, `AUTO` always resolves to `SequenceStyleGenerator`.
If the underlying database supports sequences, then a SEQUENCE generator is used. Otherwise, a TABLE generator is going to be used instead.
`LegacyFallbackInterpreter`::
This is a legacy mechanism that was used by Hibernate prior to version 5.0 or when the <<appendices/Configurations.adoc#configurations-mapping,`hibernate.id.new_generator_mappings`>> configuration property is false.
The legacy strategy maps `AUTO` to the `native` generator strategy which uses the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/dialect/Dialect.html#getNativeIdentifierGeneratorStrategy--[Dialect#getNativeIdentifierGeneratorStrategy] to resolve the actual identifier generator (e.g. `identity` or `sequence`).
[[identifiers-generators-sequence]]
==== Using sequences
For implementing database sequence-based identifier value generation Hibernate makes use of its `org.hibernate.id.enhanced.SequenceStyleGenerator` id generator.
It is important to note that SequenceStyleGenerator is capable of working against databases that do not support sequences by switching to a table as the underlying backing.
This gives Hibernate a huge degree of portability across databases while still maintaining consistent id generation behavior (versus say choosing between SEQUENCE and IDENTITY).
This backing storage is completely transparent to the user.
The preferred (and portable) way to configure this generator is using the Jakarta Persistence-defined `jakarta.persistence.SequenceGenerator` annotation.
The simplest form is to simply request sequence generation; Hibernate will use a single, implicitly-named sequence (`hibernate_sequence`) for all such unnamed definitions.
[[identifiers-generators-sequence-unnamed]]
.Unnamed sequence
====
[source,java]
----
include::{sourcedir}/SequenceGeneratorUnnamedTest.java[tag=identifiers-generators-sequence-mapping-example, indent=0]
----
====
Using `jakarta.persistence.SequenceGenerator`, you can specify a specific database sequence name.
[[identifiers-generators-sequence-named]]
.Named sequence
====
[source,java]
----
include::{sourcedir}/SequenceGeneratorNamedTest.java[tag=identifiers-generators-sequence-mapping-example, indent=0]
----
====
The `jakarta.persistence.SequenceGenerator` annotation allows you to specify additional configurations as well.
[[identifiers-generators-sequence-configured]]
.Configured sequence
====
[source,java]
----
include::{sourcedir}/SequenceGeneratorConfiguredTest.java[tag=identifiers-generators-sequence-mapping-example, indent=0]
----
====
[[identifiers-generators-identity]]
==== Using IDENTITY columns
For implementing identifier value generation based on IDENTITY columns,
Hibernate makes use of its `org.hibernate.id.IdentityGenerator` id generator which expects the identifier to be generated by INSERT into the table.
IdentityGenerator understands 3 different ways that the INSERT-generated value might be retrieved:
* If Hibernate believes the JDBC environment supports `java.sql.Statement#getGeneratedKeys`, then that approach will be used for extracting the IDENTITY generated keys.
* Otherwise, if `Dialect#supportsInsertSelectIdentity` reports true, Hibernate will use the Dialect specific INSERT+SELECT statement syntax.
* Otherwise, Hibernate will expect that the database supports some form of asking for the most recently inserted IDENTITY value via a separate SQL command as indicated by `Dialect#getIdentitySelectString`.
[IMPORTANT]
====
It is important to realize that using IDENTITY columns imposes a runtime behavior where the entity row *must* be physically inserted prior to the identifier value being known.
This can mess up extended persistence contexts (long conversations).
Because of the runtime imposition/inconsistency, Hibernate suggests other forms of identifier value generation be used (e.g. SEQUENCE).
====
[NOTE]
====
@ -437,7 +464,7 @@ If no table name is given Hibernate assumes an implicit name of `hibernate_seque
Additionally, because no `jakarta.persistence.TableGenerator#pkColumnValue` is specified,
Hibernate will use the default segment (`sequence_name='default'`) from the hibernate_sequences table.
However, you can configure the table identifier generator using the {jpaJavadocUrlPrefix}TableGenerator.html[`@TableGenerator`] annotation.
However, you can configure the table identifier generator using the {jpaJavadocUrl}/TableGenerator.html[`@TableGenerator`] annotation.
[[identifiers-generators-table-configured-mapping-example]]
.Configured table generator
@ -475,6 +502,9 @@ include::{extrasdir}/id/identifiers-generators-table-configured-persist-example.
As mentioned above, Hibernate supports UUID identifier value generation.
This is supported through its `org.hibernate.id.UUIDGenerator` id generator.
NOTE:: `org.hibernate.id.UUIDGenerator` is an example of `@IdGeneratorType` discussed in <<identifiers-generators-IdGeneratorType>>
`UUIDGenerator` supports pluggable strategies for exactly how the UUID is generated.
These strategies are defined by the `org.hibernate.id.UUIDGenerationStrategy` contract.
The default strategy is a version 4 (random) strategy according to IETF RFC 4122.
@ -536,13 +566,47 @@ These optimizers are not recommended for use. They are maintained (and mentioned
Applications can also implement and use their own optimizer strategies, as defined by the `org.hibernate.id.enhanced.Optimizer` contract.
====
[[identifiers-generators-IdGeneratorType]]
==== Using `@IdGeneratorType`
`@IdGeneratorType` is a meta-annotation that allows the creation of custom annotations that support simple, concise
and type-safe definition and configuration of custom `org.hibernate.id.IdentifierGenerator` implementations.
[[identifiers-IdGeneratorType-example]]
.@IdGeneratorType
====
[source,java]
----
include::{coreTestsDir}/org/hibernate/orm/test/id/custom/CustomSequenceGenerator.java[tag=identifiers-IdGeneratorType-example, indent=0]
include::{coreTestsDir}/org/hibernate/orm/test/id/custom/Sequence.java[tag=identifiers-IdGeneratorType-example, indent=0]
----
====
The example illustrates using `@IdGeneratorType` to define a custom sequence-based annotation `@Sequence`
to apply and configure a custom `IdentifierGenerator` implementation `CustomSequenceGenerator`.
Notice the `CustomSequenceGenerator` constructor. Custom generator defined through `@IdGeneratorType`
receive the following arguments:
1. The configuration annotation - here, `@Sequence`. This is the type-safety aspect, rather than
relying on untyped configuration properties in a Map, etc.
2. The `Member` to which annotation was applied. This allows access to the Java type of the identifier
attribute, etc.
3. `CustomIdGeneratorCreationContext` is a "parameter object" providing access to things often useful
for identifier generators.
[[identifiers-generators-GenericGenerator]]
==== Using `@GenericGenerator`
`@GenericGenerator` allows integration of any Hibernate `org.hibernate.id.IdentifierGenerator` implementation, including any of the specific ones discussed here and any custom ones.
[TIP]
====
`@GenericGenerator` is generally considered deprecated in favor of <<identifiers-generators-IdGeneratorType,@IdGeneratorType>>
====
To make use of the pooled or pooled-lo optimizers,
the entity mapping must use the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/GenericGenerator.html[`@GenericGenerator`] annotation:
`@GenericGenerator` allows integration of any Hibernate `org.hibernate.id.IdentifierGenerator` implementation, including any of the specific ones discussed here and any custom ones.
[[identifiers-generators-pooled-lo-optimizer-mapping-example]]
.Pooled-lo optimizer mapping using `@GenericGenerator` mapping

View File

@ -0,0 +1,89 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.userguide.mapping.identifier;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.junit.Test;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import static jakarta.persistence.GenerationType.SEQUENCE;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
@RequiresDialectFeature(DialectChecks.SupportsSequences.class)
public class SequenceGeneratorAnnotationNameTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Product.class
};
}
@Test
public void test() {
doInJPA(this::entityManagerFactory, entityManager -> {
for (long i = 1; i <= 5; i++) {
if(i % 3 == 0) {
entityManager.flush();
}
Product product = new Product();
product.setName(String.format("Product %d", i));
entityManager.persist(product);
}
});
}
//tag::identifiers-generators-sequence-mapping-example[]
@Entity(name = "Product")
public static class Product {
@Id
@GeneratedValue(
strategy = SEQUENCE,
generator = "explicit_product_sequence"
)
private Long id;
@Column(name = "product_name")
private String name;
//Getters and setters are omitted for brevity
//end::identifiers-generators-sequence-mapping-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//tag::identifiers-generators-sequence-mapping-example[]
}
//end::identifiers-generators-sequence-mapping-example[]
}

View File

@ -59,7 +59,7 @@ public class SequenceGeneratorConfiguredTest extends BaseEntityManagerFunctional
)
@SequenceGenerator(
name = "sequence-generator",
sequenceName = "product_sequence",
sequenceName = "explicit_product_sequence",
allocationSize = 5
)
private Long id;

View File

@ -19,6 +19,7 @@ import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.junit.Test;
import static jakarta.persistence.GenerationType.SEQUENCE;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
@ -54,12 +55,12 @@ public class SequenceGeneratorNamedTest extends BaseEntityManagerFunctionalTestC
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
strategy = SEQUENCE,
generator = "sequence-generator"
)
@SequenceGenerator(
name = "sequence-generator",
sequenceName = "product_sequence"
sequenceName = "explicit_product_sequence"
)
private Long id;

View File

@ -18,6 +18,7 @@ import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.junit.Test;
import static jakarta.persistence.GenerationType.SEQUENCE;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
@ -52,9 +53,7 @@ public class SequenceGeneratorUnnamedTest extends BaseEntityManagerFunctionalTes
public static class Product {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE
)
@GeneratedValue( strategy = SEQUENCE )
private Long id;
@Column(name = "product_name")

View File

@ -38,12 +38,12 @@ public class CustomGeneratorTests {
@Test
public void basicUseTest(SessionFactoryScope scope) {
assertThat( SimpleSequenceGenerator.generationCount ).isEqualTo( 0 );
assertThat( CustomSequenceGenerator.generationCount ).isEqualTo( 0 );
scope.inTransaction( (session) -> {
session.persist( new TheEntity( "steve" ) );
} );
assertThat( SimpleSequenceGenerator.generationCount ).isEqualTo( 1 );
assertThat( CustomSequenceGenerator.generationCount ).isEqualTo( 1 );
}
}

View File

@ -21,13 +21,22 @@ import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext;
/**
* An example custom generator.
*/
public class SimpleSequenceGenerator implements IdentifierGenerator {
//tag::identifiers-IdGeneratorType-example[]
public class CustomSequenceGenerator implements IdentifierGenerator {
//end::identifiers-IdGeneratorType-example[]
public static int generationCount = 0;
private final Identifier sequenceName;
private final String sqlSelectFrag;
public SimpleSequenceGenerator(Sequence config, Member annotatedMember, CustomIdGeneratorCreationContext context) {
//tag::identifiers-IdGeneratorType-example[]
public CustomSequenceGenerator(
Sequence config,
Member annotatedMember,
CustomIdGeneratorCreationContext context) {
//...
//end::identifiers-IdGeneratorType-example[]
final String name = config.name();
// ignore the other config for now...
@ -52,10 +61,16 @@ public class SimpleSequenceGenerator implements IdentifierGenerator {
.getDialect()
.getSequenceSupport()
.getSequenceNextValString( sequenceName.render( database.getDialect() ) );
//tag::identifiers-IdGeneratorType-example[]
}
@Override
public Object generate(SharedSessionContractImplementor session, Object object) {
public Object generate(
SharedSessionContractImplementor session,
Object object) {
//...
//end::identifiers-IdGeneratorType-example[]
generationCount++;
try {
final PreparedStatement st = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sqlSelectFrag );
@ -88,4 +103,6 @@ public class SimpleSequenceGenerator implements IdentifierGenerator {
);
}
}
//tag::identifiers-IdGeneratorType-example[]
}
//end::identifiers-IdGeneratorType-example[]

View File

@ -16,12 +16,14 @@ import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@IdGeneratorType( SimpleSequenceGenerator.class )
//tag::identifiers-IdGeneratorType-example[]
@IdGeneratorType( CustomSequenceGenerator.class )
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Sequence {
String name();
int startWith() default 1;
int incrementBy() default 50;
Class<? extends Optimizer> optimizerStrategy() default Optimizer.class;
Class<? extends Optimizer> optimizer() default Optimizer.class;
}
//end::identifiers-IdGeneratorType-example[]