Skip to content

Commit df405d3

Browse files
committed
HHH-18620 - Add @NativeGenerator
1 parent fccbb8d commit df405d3

29 files changed

+1132
-657
lines changed

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.hibernate.type.descriptor.jdbc.JdbcType;
5252
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
5353

54+
import jakarta.persistence.GenerationType;
5455
import jakarta.persistence.TemporalType;
5556

5657
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
@@ -283,10 +284,9 @@ public boolean hasSelfReferentialForeignKeyBug() {
283284
return true;
284285
}
285286

286-
287287
@Override
288-
public String getNativeIdentifierGeneratorStrategy() {
289-
return "identity";
288+
public GenerationType getNativeValueGenerationStrategy() {
289+
return GenerationType.IDENTITY;
290290
}
291291

292292
// IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575

7676
import org.jboss.logging.Logger;
7777

78+
import jakarta.persistence.GenerationType;
7879
import jakarta.persistence.TemporalType;
7980

8081
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
@@ -654,8 +655,8 @@ public boolean requiresParensForTupleDistinctCounts() {
654655
}
655656

656657
@Override
657-
public String getNativeIdentifierGeneratorStrategy() {
658-
return "sequence";
658+
public GenerationType getNativeValueGenerationStrategy() {
659+
return GenerationType.SEQUENCE;
659660
}
660661

661662
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
101101
import org.hibernate.type.spi.TypeConfiguration;
102102

103+
import jakarta.persistence.GenerationType;
103104
import jakarta.persistence.TemporalType;
104105

105106
import static java.util.regex.Pattern.CASE_INSENSITIVE;
@@ -962,8 +963,8 @@ public AggregateSupport getAggregateSupport() {
962963
}
963964

964965
@Override
965-
public String getNativeIdentifierGeneratorStrategy() {
966-
return "sequence";
966+
public GenerationType getNativeValueGenerationStrategy() {
967+
return GenerationType.SEQUENCE;
967968
}
968969

969970
// features which change between 8i, 9i, and 10g ~~~~~~~~~~~~~~~~~~~~~~~~~~

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
9494
import org.hibernate.type.spi.TypeConfiguration;
9595

96+
import jakarta.persistence.GenerationType;
9697
import jakarta.persistence.TemporalType;
9798

9899
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
@@ -894,8 +895,8 @@ public boolean supportsCaseInsensitiveLike() {
894895
}
895896

896897
@Override
897-
public String getNativeIdentifierGeneratorStrategy() {
898-
return "sequence";
898+
public GenerationType getNativeValueGenerationStrategy() {
899+
return GenerationType.SEQUENCE;
899900
}
900901

901902
@Override
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.annotations;
6+
7+
import java.lang.annotation.Target;
8+
import java.lang.annotation.Retention;
9+
10+
import org.hibernate.Incubating;
11+
import org.hibernate.dialect.Dialect;
12+
13+
import jakarta.persistence.SequenceGenerator;
14+
import jakarta.persistence.TableGenerator;
15+
16+
import static java.lang.annotation.ElementType.FIELD;
17+
import static java.lang.annotation.ElementType.METHOD;
18+
import static java.lang.annotation.ElementType.PACKAGE;
19+
import static java.lang.annotation.ElementType.TYPE;
20+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
21+
22+
/**
23+
* Generator that picks a strategy based on the {@linkplain Dialect#getNativeValueGenerationStrategy() dialect}.
24+
*
25+
* @since 7.0
26+
* @author Steve Ebersole
27+
*/
28+
@Target({METHOD, FIELD, TYPE, PACKAGE})
29+
@Retention(RUNTIME)
30+
@IdGeneratorType(org.hibernate.id.NativeGenerator.class)
31+
@Incubating
32+
public @interface NativeGenerator {
33+
SequenceGenerator sequenceForm() default @SequenceGenerator();
34+
TableGenerator tableForm() default @TableGenerator();
35+
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.boot.model.internal;
6+
7+
import java.lang.annotation.Annotation;
8+
import java.util.HashMap;
9+
import java.util.List;
10+
import java.util.Map;
11+
12+
import org.hibernate.MappingException;
13+
import org.hibernate.annotations.IdGeneratorType;
14+
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
15+
import org.hibernate.boot.model.relational.Database;
16+
import org.hibernate.boot.spi.MetadataBuildingContext;
17+
import org.hibernate.dialect.Dialect;
18+
import org.hibernate.generator.Generator;
19+
import org.hibernate.internal.util.collections.CollectionHelper;
20+
import org.hibernate.mapping.Column;
21+
import org.hibernate.mapping.PersistentClass;
22+
import org.hibernate.mapping.SimpleValue;
23+
import org.hibernate.models.spi.AnnotationTarget;
24+
import org.hibernate.models.spi.ClassDetails;
25+
import org.hibernate.models.spi.MemberDetails;
26+
27+
import jakarta.persistence.GeneratedValue;
28+
29+
import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.handleIdGeneratorType;
30+
import static org.hibernate.boot.model.internal.GeneratorParameters.identityTablesString;
31+
import static org.hibernate.boot.model.internal.GeneratorStrategies.mapLegacyNamedGenerator;
32+
import static org.hibernate.id.IdentifierGenerator.ENTITY_NAME;
33+
import static org.hibernate.id.IdentifierGenerator.JPA_ENTITY_NAME;
34+
import static org.hibernate.id.OptimizableGenerator.IMPLICIT_NAME_BASE;
35+
import static org.hibernate.id.PersistentIdentifierGenerator.PK;
36+
import static org.hibernate.id.PersistentIdentifierGenerator.TABLE;
37+
import static org.hibernate.id.PersistentIdentifierGenerator.TABLES;
38+
39+
/**
40+
* Template support for IdGeneratorResolver implementations dealing with entity identifiers
41+
*
42+
* @author Steve Ebersole
43+
*/
44+
public abstract class AbstractEntityIdGeneratorResolver implements IdGeneratorResolver {
45+
protected final PersistentClass entityMapping;
46+
protected final SimpleValue idValue;
47+
protected final MemberDetails idMember;
48+
protected final GeneratedValue generatedValue;
49+
protected final MetadataBuildingContext buildingContext;
50+
51+
public AbstractEntityIdGeneratorResolver(
52+
PersistentClass entityMapping,
53+
SimpleValue idValue,
54+
MemberDetails idMember,
55+
GeneratedValue generatedValue,
56+
MetadataBuildingContext buildingContext) {
57+
this.entityMapping = entityMapping;
58+
this.idValue = idValue;
59+
this.idMember = idMember;
60+
this.generatedValue = generatedValue;
61+
this.buildingContext = buildingContext;
62+
}
63+
64+
@Override
65+
public final void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
66+
switch ( generatedValue.strategy() ) {
67+
case UUID -> GeneratorAnnotationHelper.handleUuidStrategy( idValue, idMember, buildingContext );
68+
case IDENTITY -> GeneratorAnnotationHelper.handleIdentityStrategy( idValue );
69+
case SEQUENCE -> handleSequenceStrategy();
70+
case TABLE -> handleTableStrategy();
71+
case AUTO -> handleAutoStrategy();
72+
}
73+
}
74+
75+
private void handleSequenceStrategy() {
76+
if ( generatedValue.generator().isEmpty() ) {
77+
handleUnnamedSequenceGenerator();
78+
}
79+
else {
80+
handleNamedSequenceGenerator();
81+
}
82+
}
83+
84+
protected abstract void handleUnnamedSequenceGenerator();
85+
86+
protected abstract void handleNamedSequenceGenerator();
87+
88+
private void handleTableStrategy() {
89+
if ( generatedValue.generator().isEmpty() ) {
90+
handleUnnamedTableGenerator();
91+
}
92+
else {
93+
handleNamedTableGenerator();
94+
}
95+
}
96+
97+
protected abstract void handleUnnamedTableGenerator();
98+
99+
protected abstract void handleNamedTableGenerator();
100+
101+
private void handleAutoStrategy() {
102+
if ( generatedValue.generator().isEmpty() ) {
103+
handleUnnamedAutoGenerator();
104+
}
105+
else {
106+
handleNamedAutoGenerator();
107+
}
108+
}
109+
110+
protected abstract void handleUnnamedAutoGenerator();
111+
112+
protected abstract void handleNamedAutoGenerator();
113+
114+
protected boolean handleAsMetaAnnotated() {
115+
final Annotation fromMember = findGeneratorAnnotation( idMember );
116+
if ( fromMember != null ) {
117+
handleIdGeneratorType( fromMember, idValue, idMember, buildingContext );
118+
return true;
119+
}
120+
121+
final Annotation fromClass = findGeneratorAnnotation( idMember.getDeclaringType() );
122+
if ( fromClass != null ) {
123+
handleIdGeneratorType( fromClass, idValue, idMember, buildingContext );
124+
return true;
125+
}
126+
127+
final ClassDetails packageInfoDetails = GeneratorAnnotationHelper.locatePackageInfoDetails( idMember.getDeclaringType(), buildingContext );
128+
if ( packageInfoDetails != null ) {
129+
final Annotation fromPackage = findGeneratorAnnotation( packageInfoDetails );
130+
if ( fromPackage != null ) {
131+
handleIdGeneratorType( fromPackage, idValue, idMember, buildingContext );
132+
return true;
133+
}
134+
}
135+
136+
return false;
137+
}
138+
139+
private Annotation findGeneratorAnnotation(AnnotationTarget annotationTarget) {
140+
final List<? extends Annotation> metaAnnotated = annotationTarget.getMetaAnnotated( IdGeneratorType.class, buildingContext.getMetadataCollector().getSourceModelBuildingContext() );
141+
if ( CollectionHelper.size( metaAnnotated ) > 0 ) {
142+
return metaAnnotated.get( 0 );
143+
}
144+
145+
return null;
146+
}
147+
148+
protected boolean handleAsLegacyGenerator() {
149+
// Handle a few legacy Hibernate generators...
150+
final String nameFromGeneratedValue = generatedValue.generator();
151+
if ( !nameFromGeneratedValue.isEmpty() ) {
152+
final Class<? extends Generator> legacyNamedGenerator = mapLegacyNamedGenerator( nameFromGeneratedValue, idValue );
153+
if ( legacyNamedGenerator != null ) {
154+
final Map<String,String> configuration = buildLegacyGeneratorConfig();
155+
//noinspection unchecked,rawtypes
156+
GeneratorBinder.createGeneratorFrom(
157+
new IdentifierGeneratorDefinition( nameFromGeneratedValue, legacyNamedGenerator.getName(), configuration ),
158+
idValue,
159+
(Map) configuration,
160+
buildingContext
161+
);
162+
return true;
163+
}
164+
}
165+
166+
return false;
167+
}
168+
169+
private HashMap<String, String> buildLegacyGeneratorConfig() {
170+
final Database database = buildingContext.getMetadataCollector().getDatabase();
171+
final Dialect dialect = database.getDialect();
172+
173+
final HashMap<String, String> configuration = new HashMap<>();
174+
175+
final String tableName = idValue.getTable().getQuotedName( dialect );
176+
configuration.put( TABLE, tableName );
177+
178+
final Column idColumn = (Column) idValue.getSelectables().get( 0);
179+
final String idColumnName = idColumn.getQuotedName( dialect );
180+
configuration.put( PK, idColumnName );
181+
182+
configuration.put( ENTITY_NAME, entityMapping.getEntityName() );
183+
configuration.put( JPA_ENTITY_NAME, entityMapping.getJpaEntityName() );
184+
185+
// The table name is not really a good default for subselect entities,
186+
// so use the JPA entity name which is short
187+
configuration.put(
188+
IMPLICIT_NAME_BASE,
189+
idValue.getTable().isSubselect()
190+
? entityMapping.getJpaEntityName()
191+
: idValue.getTable().getName()
192+
);
193+
194+
configuration.put( TABLES, identityTablesString( dialect, entityMapping.getRootClass() ) );
195+
196+
return configuration;
197+
}
198+
}

0 commit comments

Comments
 (0)