HHH-12125 - Support @GeneratedValue without explicit generator definition

This commit is contained in:
Steve Ebersole 2017-11-25 12:13:12 -06:00
parent 9a75fa8d97
commit 99428251c4
8 changed files with 138 additions and 27 deletions

View File

@ -16,6 +16,7 @@ import org.hibernate.boot.model.IdGeneratorStrategyInterpreter;
import org.hibernate.boot.model.IdentifierGeneratorDefinition; import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.BinderHelper; import org.hibernate.cfg.BinderHelper;
import org.hibernate.id.IncrementGenerator;
import org.hibernate.id.MultipleHiLoPerTableGenerator; import org.hibernate.id.MultipleHiLoPerTableGenerator;
import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.SequenceHiLoGenerator; import org.hibernate.id.SequenceHiLoGenerator;
@ -109,13 +110,17 @@ public class IdGeneratorInterpreterImpl implements IdGeneratorStrategyInterprete
} }
default: { default: {
// AUTO // AUTO
if ( "increment".equalsIgnoreCase( context.getGeneratedValueGeneratorName() ) ) {
return IncrementGenerator.class.getName();
}
final Class javaType = context.getIdType(); final Class javaType = context.getIdType();
if ( UUID.class.isAssignableFrom( javaType ) ) { if ( UUID.class.isAssignableFrom( javaType ) ) {
return UUIDGenerator.class.getName(); return UUIDGenerator.class.getName();
} }
else {
return "native"; return "native";
}
} }
} }
} }
@ -217,13 +222,17 @@ public class IdGeneratorInterpreterImpl implements IdGeneratorStrategyInterprete
} }
default: { default: {
// AUTO // AUTO
if ( "increment".equalsIgnoreCase( context.getGeneratedValueGeneratorName() ) ) {
return IncrementGenerator.class.getName();
}
final Class javaType = context.getIdType(); final Class javaType = context.getIdType();
if ( UUID.class.isAssignableFrom( javaType ) ) { if ( UUID.class.isAssignableFrom( javaType ) ) {
return UUIDGenerator.class.getName(); return UUIDGenerator.class.getName();
} }
else {
return org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName(); return org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName();
}
} }
} }
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.boot.model; package org.hibernate.boot.model;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType; import javax.persistence.GenerationType;
import javax.persistence.SequenceGenerator; import javax.persistence.SequenceGenerator;
import javax.persistence.TableGenerator; import javax.persistence.TableGenerator;
@ -16,8 +17,17 @@ import javax.persistence.TableGenerator;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface IdGeneratorStrategyInterpreter { public interface IdGeneratorStrategyInterpreter {
public static interface GeneratorNameDeterminationContext { interface GeneratorNameDeterminationContext {
public Class getIdType(); /**
* The Java type of the attribute defining the id whose value is to
* be generated.
*/
Class getIdType();
/**
* The {@link GeneratedValue#generator()} name.
*/
String getGeneratedValueGeneratorName();
} }
/** /**
@ -25,8 +35,18 @@ public interface IdGeneratorStrategyInterpreter {
* GenerationType, returning {@code null} to indicate that this interpreter * GenerationType, returning {@code null} to indicate that this interpreter
* did not have a match and that any additional resolutions should be performed. * did not have a match and that any additional resolutions should be performed.
* *
* @apiNote Not really a great name as it is a bit confusing. What is really
* being resolved here is the name of the
* {@link org.hibernate.id.IdentifierGenerator} to use. This is (generally)
* different than the {@link GeneratedValue#generator()} name for the
* {@link GeneratedValue} that is the source of the passed {@link GenerationType}.
* For implementations that need it, the {@link GeneratedValue#generator()}
* is passed as part of the `context`.
*
* @param generationType The {@link javax.persistence.GeneratedValue#strategy} value * @param generationType The {@link javax.persistence.GeneratedValue#strategy} value
* @param context The context for resolution (method parameter object) * @param context The context for resolution (method parameter object)
*
* @return The {@link org.hibernate.id.IdentifierGenerator} name (FQN, short name, etc)
*/ */
String determineGeneratorName(GenerationType generationType, GeneratorNameDeterminationContext context); String determineGeneratorName(GenerationType generationType, GeneratorNameDeterminationContext context);

View File

@ -35,7 +35,6 @@ import javax.persistence.EmbeddedId;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.IdClass; import javax.persistence.IdClass;
import javax.persistence.InheritanceType; import javax.persistence.InheritanceType;
@ -146,11 +145,8 @@ import org.hibernate.cfg.annotations.QueryBinder;
import org.hibernate.cfg.annotations.SimpleValueBinder; import org.hibernate.cfg.annotations.SimpleValueBinder;
import org.hibernate.cfg.annotations.TableBinder; import org.hibernate.cfg.annotations.TableBinder;
import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
@ -2386,7 +2382,7 @@ public final class AnnotationBinder {
GeneratedValue generatedValue = idXProperty.getAnnotation( GeneratedValue.class ); GeneratedValue generatedValue = idXProperty.getAnnotation( GeneratedValue.class );
String generatorType = generatedValue != null String generatorType = generatedValue != null
? generatorType( generatedValue.strategy(), buildingContext, entityXClass ) ? generatorType( generatedValue, buildingContext, entityXClass )
: "assigned"; : "assigned";
String generatorName = generatedValue != null String generatorName = generatedValue != null
? generatedValue.generator() ? generatedValue.generator()
@ -2410,11 +2406,11 @@ public final class AnnotationBinder {
} }
public static String generatorType( public static String generatorType(
GenerationType generatorEnum, GeneratedValue generatedValueAnn,
final MetadataBuildingContext buildingContext, final MetadataBuildingContext buildingContext,
final XClass javaTypeXClass) { final XClass javaTypeXClass) {
return buildingContext.getBuildingOptions().getIdGenerationTypeInterpreter().determineGeneratorName( return buildingContext.getBuildingOptions().getIdGenerationTypeInterpreter().determineGeneratorName(
generatorEnum, generatedValueAnn.strategy(),
new IdGeneratorStrategyInterpreter.GeneratorNameDeterminationContext() { new IdGeneratorStrategyInterpreter.GeneratorNameDeterminationContext() {
Class javaType = null; Class javaType = null;
@Override @Override
@ -2426,6 +2422,11 @@ public final class AnnotationBinder {
} }
return javaType; return javaType;
} }
@Override
public String getGeneratedValueGeneratorName() {
return generatedValueAnn.generator();
}
} }
); );
} }
@ -2739,7 +2740,7 @@ public final class AnnotationBinder {
GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class ); GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
String generatorType = generatedValue != null String generatorType = generatedValue != null
? generatorType( generatedValue.strategy(), buildingContext, property.getType() ) ? generatorType( generatedValue, buildingContext, property.getType() )
: "assigned"; : "assigned";
String generator = generatedValue != null ? generatedValue.generator() : BinderHelper.ANNOTATION_STRING_DEFAULT; String generator = generatedValue != null ? generatedValue.generator() : BinderHelper.ANNOTATION_STRING_DEFAULT;

View File

@ -1735,6 +1735,10 @@ public interface AvailableSettings {
* True/false setting indicating whether the value specified for {@link GeneratedValue#generator()} * True/false setting indicating whether the value specified for {@link GeneratedValue#generator()}
* should be used as the sequence/table name when no matching {@link javax.persistence.SequenceGenerator} * should be used as the sequence/table name when no matching {@link javax.persistence.SequenceGenerator}
* or {@link javax.persistence.TableGenerator} is found. * or {@link javax.persistence.TableGenerator} is found.
*
* The default value is `true` meaning that {@link GeneratedValue#generator()} will be used as the
* sequence/table name by default. Users migrating from earlier versions using the legacy
* `hibernate_sequence` name should disable this setting,
*/ */
String PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME = "hibernate.model.generator_name_as_sequence_name"; String PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME = "hibernate.model.generator_name_as_sequence_name";
} }

View File

@ -877,7 +877,19 @@ public class BinderHelper {
else { else {
strategyName = generationInterpreter.determineGeneratorName( strategyName = generationInterpreter.determineGeneratorName(
generationType, generationType,
() -> buildingContext.getBuildingOptions().getReflectionManager().toClass( idXProperty.getType() ) new IdGeneratorStrategyInterpreter.GeneratorNameDeterminationContext() {
@Override
public Class getIdType() {
return buildingContext.getBuildingOptions()
.getReflectionManager()
.toClass( idXProperty.getType() );
}
@Override
public String getGeneratedValueGeneratorName() {
return generatedValueAnn.generator();
}
}
); );
} }

View File

@ -280,7 +280,7 @@ public class SequenceStyleGenerator
String fallbackSequenceName = DEF_SEQUENCE_NAME; String fallbackSequenceName = DEF_SEQUENCE_NAME;
final Boolean preferGeneratorNameAsDefaultName = serviceRegistry.getService( ConfigurationService.class ) final Boolean preferGeneratorNameAsDefaultName = serviceRegistry.getService( ConfigurationService.class )
.getSetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, StandardConverters.BOOLEAN ); .getSetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, StandardConverters.BOOLEAN, true );
if ( preferGeneratorNameAsDefaultName != null && preferGeneratorNameAsDefaultName ) { if ( preferGeneratorNameAsDefaultName != null && preferGeneratorNameAsDefaultName ) {
final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME ); final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME );
if ( StringHelper.isNotEmpty( generatorName ) ) { if ( StringHelper.isNotEmpty( generatorName ) ) {

View File

@ -394,7 +394,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
if ( tableName == null ) { if ( tableName == null ) {
tableName = DEF_TABLE; tableName = DEF_TABLE;
final Boolean preferGeneratorNameAsDefaultName = serviceRegistry.getService( ConfigurationService.class ) final Boolean preferGeneratorNameAsDefaultName = serviceRegistry.getService( ConfigurationService.class )
.getSetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, StandardConverters.BOOLEAN ); .getSetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, StandardConverters.BOOLEAN, true );
if ( preferGeneratorNameAsDefaultName != null && preferGeneratorNameAsDefaultName ) { if ( preferGeneratorNameAsDefaultName != null && preferGeneratorNameAsDefaultName ) {
final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME ); final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME );
if ( StringHelper.isNotEmpty( generatorName ) ) { if ( StringHelper.isNotEmpty( generatorName ) ) {

View File

@ -12,6 +12,7 @@ import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.SequenceGenerator; import javax.persistence.SequenceGenerator;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources; import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistry;
@ -19,6 +20,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IncrementGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableGenerator; import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
@ -33,6 +35,9 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
/** /**
* Tests of various aspects of {@link GeneratedValue} handling in regards to determining
* the {@link IdentifierGenerator} to use
*
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class GeneratedValueTests extends BaseUnitTestCase { public class GeneratedValueTests extends BaseUnitTestCase {
@ -60,7 +65,9 @@ public class GeneratedValueTests extends BaseUnitTestCase {
@Test @Test
public void testImplicitSequenceGenerator() { public void testImplicitSequenceGenerator() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build(); final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, "false" )
.build();
final Metadata bootModel = new MetadataSources( ssr ) final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ImplicitSequenceGeneratorEntity.class ) .addAnnotatedClass( ImplicitSequenceGeneratorEntity.class )
.buildMetadata(); .buildMetadata();
@ -81,9 +88,7 @@ public class GeneratedValueTests extends BaseUnitTestCase {
@Test @Test
public void testImplicitSequenceGeneratorGeneratorName() { public void testImplicitSequenceGeneratorGeneratorName() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
.applySetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, "true" )
.build();
final Metadata bootModel = new MetadataSources( ssr ) final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ImplicitSequenceGeneratorEntity.class ) .addAnnotatedClass( ImplicitSequenceGeneratorEntity.class )
.buildMetadata(); .buildMetadata();
@ -105,7 +110,9 @@ public class GeneratedValueTests extends BaseUnitTestCase {
@Test @Test
public void testExplicitSequenceGeneratorImplicitName() { public void testExplicitSequenceGeneratorImplicitName() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build(); final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, "false" )
.build();
final Metadata bootModel = new MetadataSources( ssr ) final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ExplicitSequenceGeneratorImplicitNameEntity.class ) .addAnnotatedClass( ExplicitSequenceGeneratorImplicitNameEntity.class )
.buildMetadata(); .buildMetadata();
@ -127,9 +134,8 @@ public class GeneratedValueTests extends BaseUnitTestCase {
@Test @Test
public void testExplicitSequenceGeneratorImplicitNamePreferGeneratorName() { public void testExplicitSequenceGeneratorImplicitNamePreferGeneratorName() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() // this should be the default behavior
.applySetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, "true" ) final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
.build();
final Metadata bootModel = new MetadataSources( ssr ) final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ExplicitSequenceGeneratorImplicitNameEntity.class ) .addAnnotatedClass( ExplicitSequenceGeneratorImplicitNameEntity.class )
.buildMetadata(); .buildMetadata();
@ -171,6 +177,42 @@ public class GeneratedValueTests extends BaseUnitTestCase {
assertThat( tableGenerator.getIncrementSize(), is( 50 ) ); assertThat( tableGenerator.getIncrementSize(), is( 50 ) );
} }
@Test
public void testExplicitIncrementGenerator() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ExplicitIncrementGeneratorEntity.class )
.buildMetadata();
final PersistentClass entityMapping = bootModel.getEntityBinding( ExplicitIncrementGeneratorEntity.class.getName() );
final IdentifierGenerator generator = entityMapping.getIdentifier().createIdentifierGenerator(
bootModel.getIdentifierGeneratorFactory(),
ssr.getService( JdbcEnvironment.class ).getDialect(),
null,
null,
(RootClass) entityMapping
);
assertTyping( IncrementGenerator.class, generator );
}
@Test
public void testImplicitIncrementGenerator() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ImplicitIncrementGeneratorEntity.class )
.buildMetadata();
final PersistentClass entityMapping = bootModel.getEntityBinding( ImplicitIncrementGeneratorEntity.class.getName() );
final IdentifierGenerator generator = entityMapping.getIdentifier().createIdentifierGenerator(
bootModel.getIdentifierGeneratorFactory(),
ssr.getService( JdbcEnvironment.class ).getDialect(),
null,
null,
(RootClass) entityMapping
);
assertTyping( IncrementGenerator.class, generator );
}
@Entity @Entity
public static class ExplicitGeneratorEntity { public static class ExplicitGeneratorEntity {
/** /**
@ -216,4 +258,27 @@ public class GeneratedValueTests extends BaseUnitTestCase {
public Integer id; public Integer id;
public String name; public String name;
} }
@Entity
public static class ExplicitIncrementGeneratorEntity {
/**
* This entity does not have explicit {@link javax.persistence.TableGenerator} defined
*/
@Id
@GeneratedValue( strategy = GenerationType.AUTO, generator = "increment" )
@GenericGenerator( name = "increment", strategy = "increment" )
public Integer id;
public String name;
}
@Entity
public static class ImplicitIncrementGeneratorEntity {
/**
* This entity does not have explicit {@link javax.persistence.TableGenerator} defined
*/
@Id
@GeneratedValue( strategy = GenerationType.AUTO, generator = "increment" )
public Integer id;
public String name;
}
} }