Document value generation;

Moved `@CurrentTimestamp` from src/test/java to src/main/java
This commit is contained in:
Steve Ebersole 2021-08-03 14:03:32 -05:00
parent 34a1fa8c7f
commit 53fc490181
12 changed files with 285 additions and 253 deletions

View File

@ -1,9 +1,13 @@
[[basic]]
=== Basic types
:modeldir: ../../../../../main/java/org/hibernate/userguide/model
:sourcedir: ../../../../../test/java/org/hibernate/userguide/mapping
:resourcedir: ../../../../../test/resources/org/hibernate/userguide/
:converter-sourcedir: ../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter
:rootProjectDir: ../../../../../../..
:documentationProjectDir: {rootProjectDir}/documentation
:coreProjectDir: {rootProjectDir}/hibernate-core
:core-generated-test-dir: {coreProjectDir}/src/test/java/org/hibernate/orm/test/mapping/generated
:modeldir: {documentationProjectDir}/src/main/java/org/hibernate/userguide/model
:sourcedir: {documentationProjectDir}/src/test/java/org/hibernate/userguide/mapping
:resourcedir: {documentationProjectDir}/src/test/resources/org/hibernate/userguide/
:converter-sourcedir: {coreProjectDir}/src/test/java/org/hibernate/orm/test/mapping/converted/converter
:extrasdir: extras
A basic type is a mapping between a Java type and a single database column.
@ -2082,304 +2086,163 @@ For more about quoting-related configuration properties, check out the <<appendi
[[mapping-generated]]
==== Generated properties
Generated properties are properties that have their values generated by the database.
Typically, Hibernate applications needed to `refresh` objects that contain any properties for which the database was generating values.
Marking properties as generated, however, lets the application delegate this responsibility to Hibernate.
When Hibernate issues an SQL INSERT or UPDATE for an entity that has defined generated properties, it immediately issues a select to retrieve the generated values.
NOTE:: This section talks about generating values for non-identifier attributes. For discussion of generated identifier values, see <<identifiers-generators>>.
Properties marked as generated must additionally be _non-insertable_ and _non-updateable_.
Only `@Version` and `@Basic` types can be marked as generated.
Generated attributes have their values generated as part of performing a SQL INSERT or UPDATE. Applications can generate these
values in any number of ways (SQL DEFAULT value, trigger, etc). Typically, the application needs to refresh objects that
contain any properties for which the database was generating values, which is a major drawback.
`NEVER` (the default):: the given property value is not generated within the database.
`INSERT`:: the given property value is generated on insert but is not regenerated on subsequent updates. Properties like _creationTimestamp_ fall into this category.
`ALWAYS`:: the property value is generated both on insert and update.
Applications can also delegate generation to Hibernate, in which case Hibernate will manage the value generation
and (potentialfootnote:[Only in-DB generation requires the refresh]) state refresh itself.
To mark a property as generated, use The Hibernate specific `@Generated` annotation.
[IMPORTANT]
====
Only `@Basic` and `@Version` attributes can be marked as generated.
[[mapping-generated-Generated]]
===== `@Generated` annotation
Generated attributes must additionally be _non-insertable_ and _non-updateable_.
====
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Generated.html[`@Generated`] annotation is used so that Hibernate can fetch the currently annotated property after the entity has been persisted or updated.
For this reason, the `@Generated` annotation accepts a https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/GenerationTime.html[`GenerationTime`] enum value.
Hibernate supports both in-VM and in-DB generation. A generation that uses the current JVM timestamp as the
generated value is an example of an in-VM strategy. A generation that uses the database's `current_timestamp`
function is an example of an in-DB strategy.
Considering the following entity:
Hibernate supports the following timing (when) for generation:
[[mapping-generated-Generated-example]]
.`@Generated` mapping example
`NEVER` (the default):: the given attribute value is not generated
`INSERT`:: the attribute value is generated on insert but is not regenerated on subsequent updates
`ALWAYS`:: the attribute value is generated both on insert and update.
Hibernate supports multiple ways to mark an attribute as generated:
* Using the dedicated generators provided by Hibernate
* `@CurrentTimestamp` - <<mapping-generated-CurrentTimestamp>>
* `@CreationTimestamp` - <<mapping-generated-CreationTimestamp>>
* `@UpdateTimestamp` - <<mapping-generated-UpdateTimestamp>>
* `@Generated` - <<mapping-generated-Generated>>
* `@GeneratorType` - is deprecated and not covered here
* Using a custom generation strategy - <<mapping-generated-custom>>
[[mapping-generated-CurrentTimestamp]]
===== `@CurrentTimestamp`
The `@CurrentTimestamp` annotation is an in-DB strategy that can be configured for either INSERT or ALWAYS timing.
It uses the database's `current_timestamp` function as the generated value
[[mapping-generated-provided-CurrentTimestamp-ex1]]
.`@UpdateTimestamp` mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/GeneratedTest.java[tags=mapping-generated-Generated-example]
include::{core-generated-test-dir}/CurrentTimestampAnnotationTests.java[tags=mapping-generated-CurrentTimestamp-ex1]
----
====
When the `Person` entity is persisted, Hibernate is going to fetch the calculated `fullName` column from the database,
which concatenates the first, middle, and last name.
[[mapping-generated-Generated-persist-example]]
.`@Generated` persist example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/GeneratedTest.java[tags=mapping-generated-Generated-persist-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-generated-Generated-persist-example.sql[]
----
====
The same goes when the `Person` entity is updated.
Hibernate is going to fetch the calculated `fullName` column from the database after the entity is modified.
[[mapping-generated-Generated-update-example]]
.`@Generated` update example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/GeneratedTest.java[tags=mapping-generated-Generated-update-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-generated-Generated-update-example.sql[]
----
====
[[mapping-generated-GeneratorType]]
===== `@GeneratorType` annotation
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/GeneratorType.html[`@GeneratorType`] annotation is used so that
you can provide a custom generator to set the value of the currently annotated property.
For this reason, the `@GeneratorType` annotation accepts a https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/GenerationTime.html[`GenerationTime`] enum value
and a custom https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/ValueGenerator.html[`ValueGenerator`] class type.
Considering the following entity:
[[mapping-generated-GeneratorType-example]]
.`@GeneratorType` mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/GeneratorTypeTest.java[tags=mapping-generated-GeneratorType-example]
----
====
When the `Person` entity is persisted, Hibernate is going to populate the `createdBy` column with the currently logged user.
[[mapping-generated-GeneratorType-persist-example]]
.`@GeneratorType` persist example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/GeneratorTypeTest.java[tags=mapping-generated-GeneratorType-persist-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-generated-GeneratorType-persist-example.sql[]
----
====
The same goes when the `Person` entity is updated.
Hibernate is going to populate the `updatedBy` column with the currently logged user.
[[mapping-generated-GeneratorType-update-example]]
.`@GeneratorType` update example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/GeneratorTypeTest.java[tags=mapping-generated-GeneratorType-update-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-generated-GeneratorType-update-example.sql[]
----
====
[[mapping-generated-CreationTimestamp]]
===== `@CreationTimestamp` annotation
===== `@CreationTimestamp`
The `@CreationTimestamp` annotation instructs Hibernate to set the annotated entity attribute with the current timestamp value of the JVM
when the entity is being persisted.
The `@CreationTimestamp` annotation is an in-VM `INSERT` strategy. Hibernate will use
the current timestamp of the JVM as the insert value for the attribute.
The supported property types are:
- `java.util.Date`
- `java.util.Calendar`
- `java.sql.Date`
- `java.sql.Time`
- `java.sql.Timestamp`
Supports most temporal types (`java.time.Instant`, `java.util.Date`, `java.util.Calendar`, etc)
[[mapping-generated-CreationTimestamp-example]]
.`@CreationTimestamp` mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/CreationTimestampTest.java[tags=mapping-generated-CreationTimestamp-example]
include::{sourcedir}/generated/CreationTimestampTest.java[tags=mapping-generated-provided-creation-ex1]
----
====
When the `Event` entity is persisted, Hibernate is going to populate the underlying `timestamp` column with the current JVM timestamp value:
While inserting the `Event`, Hibernate will populate the underlying `timestamp` column with the current JVM timestamp value
[[mapping-generated-CreationTimestamp-persist-example]]
.`@CreationTimestamp` persist example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/CreationTimestampTest.java[tags=mapping-generated-CreationTimestamp-persist-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-generated-CreationTimestamp-persist-example.sql[]
----
====
[[mapping-generated-UpdateTimestamp]]
===== `@UpdateTimestamp` annotation
The `@UpdateTimestamp` annotation instructs Hibernate to set the annotated entity attribute with the current timestamp value of the JVM
when the entity is being persisted.
The `@UpdateTimestamp` annotation is an in-VM `INSERT` strategy. Hibernate will use
the current timestamp of the JVM as the insert and update value for the attribute.
The supported property types are:
Supports most temporal types (`java.time.Instant`, `java.util.Date`, `java.util.Calendar`, etc)
- `java.util.Date`
- `java.util.Calendar`
- `java.sql.Date`
- `java.sql.Time`
- `java.sql.Timestamp`
[[mapping-generated-UpdateTimestamp-example]]
[[mapping-generated-provided-update-ex1]]
.`@UpdateTimestamp` mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/UpdateTimestampTest.java[tags=mapping-generated-UpdateTimestamp-example]
include::{sourcedir}/generated/UpdateTimestampTest.java[tags=mapping-generated-provided-update-ex1]
----
====
When the `Bid` entity is persisted, Hibernate is going to populate the underlying `updated_on` column with the current JVM timestamp value:
[[mapping-generated-UpdateTimestamp-persist-example]]
.`@UpdateTimestamp` persist example
[[mapping-generated-Generated]]
===== `@Generated` annotation
The `@Generated` annotation is an in-DB strategy that can be configured for either INSERT or ALWAYS timing
This is the legacy mapping for in-DB generated values.
[[mapping-generated-provided-Generated]]
.`@Generated` mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/UpdateTimestampTest.java[tags=mapping-generated-UpdateTimestamp-persist-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-generated-UpdateTimestamp-persist-example.sql[]
include::{sourcedir}/generated/GeneratedTest.java[tags=mapping-generated-provided-generated]
----
====
When updating the `Bid` entity, Hibernate is going to modify the `updated_on` column with the current JVM timestamp value:
[[mapping-generated-UpdateTimestamp-update-example]]
.`@UpdateTimestamp` update example
[[mapping-generated-custom]]
===== Custom generation strategy
Hibernate also supports value generation via a pluggable API using `@ValueGenerationType` and `AnnotationValueGeneration`
allowing users to define any generation strategy they wish.
Let's look at an example of generating UUID values. First the attribute mapping
[[mapping-generated-custom-ex1]]
.Custom generation mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/UpdateTimestampTest.java[tags=mapping-generated-UpdateTimestamp-update-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-generated-UpdateTimestamp-update-example.sql[]
include::{core-generated-test-dir}/temporals/GeneratedUuidTests.java[tags=mapping-generated-custom-ex1]
----
====
[[mapping-generated-ValueGenerationType]]
===== `@ValueGenerationType` meta-annotation
This example makes use of an annotation named `@GeneratedUuidValue` - but where is that annotation defined? This is a custom
annotations provided by the application.
Hibernate 4.3 introduced the `@ValueGenerationType` meta-annotation, which is a new approach to declaring generated attributes or customizing generators.
`@Generated` has been retrofitted to use the `@ValueGenerationType` meta-annotation.
But `@ValueGenerationType` exposes more features than what `@Generated` currently supports, and,
to leverage some of those features, you'd simply wire up a new generator annotation.
As you'll see in the following examples, the `@ValueGenerationType` meta-annotation is used when declaring the custom annotation used to mark the entity properties that need a specific generation strategy.
The actual generation logic must be added to the class that implements the `AnnotationValueGeneration` interface.
[[mapping-database-generated-value]]
====== Database-generated values
For example, let's say we want the timestamps to be generated by calls to the standard ANSI SQL function `current_timestamp` (rather than triggers or DEFAULT values):
[[mapping-database-generated-value-example]]
.A `ValueGenerationType` mapping for database generation
[[mapping-generated-custom-ex2]]
.Custom generation mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/DatabaseValueGenerationTest.java[tags=mapping-database-generated-value-example]
include::{core-generated-test-dir}/temporals/GeneratedUuidTests.java[tags=mapping-generated-custom-ex2]
----
====
When persisting an `Event` entity, Hibernate generates the following SQL statement:
The `@ValueGenerationType( generatedBy = UuidValueGeneration.class )` here is the important piece; it tells
Hibernate how to generate values for the attribute - here it will use the specified `UuidValueGeneration` class
====
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-database-generated-value-example.sql[]
----
====
As you can see, the `current_timestamp` value was used for assigning the `timestamp` column value.
[[mapping-in-memory-generated-value]]
====== In-memory-generated values
If the timestamp value needs to be generated in-memory, the following mapping must be used instead:
[[mapping-in-memory-generated-value-example]]
.A `ValueGenerationType` mapping for in-memory value generation
[[mapping-generated-custom-ex3]]
.Custom generation mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/generated/InMemoryValueGenerationTest.java[tags=mapping-in-memory-generated-value-example]
include::{core-generated-test-dir}/temporals/GeneratedUuidTests.java[tags=mapping-generated-custom-ex3]
----
====
When persisting an `Event` entity, Hibernate generates the following SQL statement:
====
[source, SQL, indent=0]
----
include::{extrasdir}/basic/mapping-in-memory-generated-value-example.sql[]
----
====
As you can see, the `new Date()` object value was used for assigning the `timestamp` column value.
See https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/ValueGenerationType.html[`@ValueGenerationType`]
and https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/tuple/AnnotationValueGeneration.html[`AnnotationValueGeneration`]
for details of each contract

View File

@ -41,7 +41,7 @@ public class CreationTimestampTest extends BaseEntityManagerFunctionalTestCase {
} );
}
//tag::mapping-generated-CreationTimestamp-example[]
//tag::mapping-generated-provided-creation-ex1[]
@Entity(name = "Event")
public static class Event {
@ -54,7 +54,7 @@ public class CreationTimestampTest extends BaseEntityManagerFunctionalTestCase {
private Date timestamp;
//Constructors, getters, and setters are omitted for brevity
//end::mapping-generated-CreationTimestamp-example[]
//end::mapping-generated-provided-creation-ex1[]
public Event() {}
@ -65,7 +65,7 @@ public class CreationTimestampTest extends BaseEntityManagerFunctionalTestCase {
public Date getTimestamp() {
return timestamp;
}
//tag::mapping-generated-CreationTimestamp-example[]
//tag::mapping-generated-provided-creation-ex1[]
}
//end::mapping-generated-CreationTimestamp-example[]
//end::mapping-generated-provided-creation-ex1[]
}

View File

@ -65,7 +65,7 @@ public class GeneratedTest extends BaseEntityManagerFunctionalTestCase {
} );
}
//tag::mapping-generated-Generated-example[]
//tag::mapping-generated-provided-generated[]
@Entity(name = "Person")
public static class Person {
@ -99,7 +99,7 @@ public class GeneratedTest extends BaseEntityManagerFunctionalTestCase {
")")
private String fullName;
//end::mapping-generated-Generated-example[]
//end::mapping-generated-provided-generated[]
public Person() {}
public Long getId() {
@ -169,7 +169,7 @@ public class GeneratedTest extends BaseEntityManagerFunctionalTestCase {
public String getFullName() {
return fullName;
}
//tag::mapping-generated-Generated-example[]
//tag::mapping-generated-provided-generated[]
}
//end::mapping-generated-Generated-example[]
//end::mapping-generated-provided-generated[]
}

View File

@ -53,7 +53,7 @@ public class UpdateTimestampTest extends BaseEntityManagerFunctionalTestCase {
} );
}
//tag::mapping-generated-UpdateTimestamp-example[]
//tag::mapping-generated-provided-update-ex1[]
@Entity(name = "Bid")
public static class Bid {
@ -72,7 +72,7 @@ public class UpdateTimestampTest extends BaseEntityManagerFunctionalTestCase {
//Getters and setters are omitted for brevity
//end::mapping-generated-UpdateTimestamp-example[]
//end::mapping-generated-provided-update-ex1[]
public Long getId() {
return id;
@ -101,7 +101,7 @@ public class UpdateTimestampTest extends BaseEntityManagerFunctionalTestCase {
public void setCents(Long cents) {
this.cents = cents;
}
//tag::mapping-generated-UpdateTimestamp-example[]
//tag::mapping-generated-provided-update-ex1[]
}
//end::mapping-generated-UpdateTimestamp-example[]
//end::mapping-generated-provided-update-ex1[]
}

View File

@ -4,19 +4,28 @@
* 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.orm.test.mapping.generated.temporals;
package org.hibernate.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.tuple.GenerationTiming;
/**
* Specifies to use the database `current_timestamp` function for generating
* values for the associated attribute based on {@link #timing()}
*
* @see CurrentTimestampGeneration
*
* @author Steve Ebersole
*/
@ValueGenerationType(generatedBy = CurrentTimestampGeneration.class)
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE } )
@Inherited
public @interface CurrentTimestamp {
GenerationTiming timing();
}

View File

@ -4,23 +4,26 @@
* 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.orm.test.mapping.generated.temporals;
package org.hibernate.annotations;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
/**
* Value generation strategy for using the database `current_timestamp` function to generate
* the values
*
* @see CurrentTimestamp
*
* @author Steve Ebersole
*/
public class CurrentTimestampGeneration implements AnnotationValueGeneration<CurrentTimestamp> {
private GenerationTiming timing;
private Class<?> propertyType;
@Override
public void initialize(CurrentTimestamp annotation, Class<?> propertyType) {
this.timing = annotation.timing();
this.propertyType = propertyType;
}
@Override
@ -30,6 +33,7 @@ public class CurrentTimestampGeneration implements AnnotationValueGeneration<Cur
@Override
public ValueGenerator<?> getValueGenerator() {
// ValueGenerator is only used for in-VM generations
return null;
}

View File

@ -11,6 +11,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.tuple.VmValueGeneration;
@ -20,10 +21,13 @@ import org.hibernate.tuple.VmValueGeneration;
* annotated property.
*
* @author Gunnar Morling
*
* @deprecated Most uses can be changed to use {@link ValueGenerationType} + {@link AnnotationValueGeneration}
*/
@ValueGenerationType( generatedBy = VmValueGeneration.class )
@Retention( RetentionPolicy.RUNTIME )
@Target( value = { ElementType.FIELD, ElementType.METHOD } )
@Deprecated
public @interface GeneratorType {
/**

View File

@ -11,7 +11,7 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.orm.test.mapping.generated.temporals.CurrentTimestamp;
import org.hibernate.annotations.CurrentTimestamp;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;
@ -24,9 +24,9 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = GeneratedValueAnnotationTests.AuditedEntity.class )
@DomainModel( annotatedClasses = CurrentTimestampAnnotationTests.AuditedEntity.class )
@SessionFactory
public class GeneratedValueAnnotationTests {
public class CurrentTimestampAnnotationTests {
@Test
public void test(SessionFactoryScope scope) {
final AuditedEntity created = scope.fromTransaction( (session) -> {
@ -71,11 +71,13 @@ public class GeneratedValueAnnotationTests {
public Integer id;
public String name;
//tag::mapping-generated-CurrentTimestamp-ex1[]
@CurrentTimestamp( timing = GenerationTiming.INSERT )
public Instant createdAt;
@CurrentTimestamp( timing = GenerationTiming.ALWAYS )
public Instant lastUpdatedAt;
//end::mapping-generated-CurrentTimestamp-ex1[]
public AuditedEntity() {
}

View File

@ -14,7 +14,7 @@ import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.orm.test.mapping.generated.temporals.CurrentTimestamp;
import org.hibernate.annotations.CurrentTimestamp;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;

View File

@ -11,6 +11,7 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CurrentTimestamp;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;

View File

@ -0,0 +1,148 @@
/*
* 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.orm.test.mapping.generated.temporals;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.Session;
import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test illustrating usage of {@link ValueGenerationType}
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = GeneratedUuidTests.GeneratedUuidEntity.class )
@SessionFactory
public class GeneratedUuidTests {
@Test
public void test(SessionFactoryScope scope) {
final GeneratedUuidEntity created = scope.fromTransaction( (session) -> {
final GeneratedUuidEntity entity = new GeneratedUuidEntity( 1, "tsifr" );
session.persist( entity );
return entity;
} );
assertThat( created.createdUuid ).isNotNull();
assertThat( created.updatedUuid ).isNotNull();
created.name = "first";
// then changing
final GeneratedUuidEntity merged = scope.fromTransaction( (session) -> {
return (GeneratedUuidEntity) session.merge( created );
} );
assertThat( merged ).isNotNull();
assertThat( merged.createdUuid ).isNotNull();
assertThat( merged.updatedUuid ).isNotNull();
assertThat( merged.createdUuid ).isEqualTo( created.createdUuid );
assertThat( merged.updatedUuid ).isNotEqualTo( created.updatedUuid );
assertThat( merged ).isNotNull();
// lastly, make sure we can load it..
final GeneratedUuidEntity loaded = scope.fromTransaction( (session) -> {
return session.get( GeneratedUuidEntity.class, 1 );
} );
assertThat( loaded ).isNotNull();
assertThat( loaded.createdUuid ).isEqualTo( merged.createdUuid );
assertThat( loaded.updatedUuid ).isEqualTo( merged.updatedUuid );
}
//tag::mapping-generated-custom-ex2[]
@ValueGenerationType( generatedBy = UuidValueGeneration.class )
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE } )
@Inherited
public @interface GeneratedUuidValue {
GenerationTiming timing();
}
//end::mapping-generated-custom-ex2[]
//tag::mapping-generated-custom-ex3[]
public static class UuidValueGeneration implements AnnotationValueGeneration<GeneratedUuidValue>, ValueGenerator<UUID> {
private GenerationTiming timing;
@Override
public void initialize(GeneratedUuidValue annotation, Class<?> propertyType) {
timing = annotation.timing();
}
@Override
public GenerationTiming getGenerationTiming() {
return timing;
}
@Override
public ValueGenerator<?> getValueGenerator() {
return this;
}
@Override
public boolean referenceColumnInSql() {
return false;
}
@Override
public String getDatabaseGeneratedReferencedColumnValue() {
return null;
}
@Override
public UUID generateValue(Session session, Object owner) {
return UUID.randomUUID();
}
}
//end::mapping-generated-custom-ex3[]
@Entity( name = "GeneratedUuidEntity" )
@Table( name = "t_gen_uuid" )
public static class GeneratedUuidEntity {
@Id
public Integer id;
@Basic
public String name;
//tag::mapping-generated-custom-ex1[]
@GeneratedUuidValue( timing = GenerationTiming.INSERT )
public UUID createdUuid;
@GeneratedUuidValue( timing = GenerationTiming.ALWAYS )
public UUID updatedUuid;
//end::mapping-generated-custom-ex1[]
public GeneratedUuidEntity() {
}
public GeneratedUuidEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
}
}

View File

@ -11,6 +11,7 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CurrentTimestamp;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.testing.orm.junit.DomainModel;