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.cfg.AvailableSettings;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.id.IncrementGenerator;
import org.hibernate.id.MultipleHiLoPerTableGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.SequenceHiLoGenerator;
@ -109,16 +110,20 @@ public class IdGeneratorInterpreterImpl implements IdGeneratorStrategyInterprete
}
default: {
// AUTO
if ( "increment".equalsIgnoreCase( context.getGeneratedValueGeneratorName() ) ) {
return IncrementGenerator.class.getName();
}
final Class javaType = context.getIdType();
if ( UUID.class.isAssignableFrom( javaType ) ) {
return UUIDGenerator.class.getName();
}
else {
return "native";
}
}
}
}
@Override
public void interpretTableGenerator(
@ -217,16 +222,20 @@ public class IdGeneratorInterpreterImpl implements IdGeneratorStrategyInterprete
}
default: {
// AUTO
if ( "increment".equalsIgnoreCase( context.getGeneratedValueGeneratorName() ) ) {
return IncrementGenerator.class.getName();
}
final Class javaType = context.getIdType();
if ( UUID.class.isAssignableFrom( javaType ) ) {
return UUIDGenerator.class.getName();
}
else {
return org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName();
}
}
}
}
@Override
public void interpretTableGenerator(

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.boot.model;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.SequenceGenerator;
import javax.persistence.TableGenerator;
@ -16,8 +17,17 @@ import javax.persistence.TableGenerator;
* @author Steve Ebersole
*/
public interface IdGeneratorStrategyInterpreter {
public static interface GeneratorNameDeterminationContext {
public Class getIdType();
interface GeneratorNameDeterminationContext {
/**
* 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
* 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 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);

View File

@ -35,7 +35,6 @@ import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
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.TableBinder;
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.id.PersistentIdentifierGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath;
@ -2386,7 +2382,7 @@ public final class AnnotationBinder {
GeneratedValue generatedValue = idXProperty.getAnnotation( GeneratedValue.class );
String generatorType = generatedValue != null
? generatorType( generatedValue.strategy(), buildingContext, entityXClass )
? generatorType( generatedValue, buildingContext, entityXClass )
: "assigned";
String generatorName = generatedValue != null
? generatedValue.generator()
@ -2410,11 +2406,11 @@ public final class AnnotationBinder {
}
public static String generatorType(
GenerationType generatorEnum,
GeneratedValue generatedValueAnn,
final MetadataBuildingContext buildingContext,
final XClass javaTypeXClass) {
return buildingContext.getBuildingOptions().getIdGenerationTypeInterpreter().determineGeneratorName(
generatorEnum,
generatedValueAnn.strategy(),
new IdGeneratorStrategyInterpreter.GeneratorNameDeterminationContext() {
Class javaType = null;
@Override
@ -2426,6 +2422,11 @@ public final class AnnotationBinder {
}
return javaType;
}
@Override
public String getGeneratedValueGeneratorName() {
return generatedValueAnn.generator();
}
}
);
}
@ -2739,7 +2740,7 @@ public final class AnnotationBinder {
GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
String generatorType = generatedValue != null
? generatorType( generatedValue.strategy(), buildingContext, property.getType() )
? generatorType( generatedValue, buildingContext, property.getType() )
: "assigned";
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()}
* should be used as the sequence/table name when no matching {@link javax.persistence.SequenceGenerator}
* 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";
}

View File

@ -877,7 +877,19 @@ public class BinderHelper {
else {
strategyName = generationInterpreter.determineGeneratorName(
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;
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 ) {
final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME );
if ( StringHelper.isNotEmpty( generatorName ) ) {

View File

@ -394,7 +394,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
if ( tableName == null ) {
tableName = DEF_TABLE;
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 ) {
final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME );
if ( StringHelper.isNotEmpty( generatorName ) ) {

View File

@ -12,6 +12,7 @@ import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
@ -19,6 +20,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IncrementGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.mapping.PersistentClass;
@ -33,6 +35,9 @@ import static org.hamcrest.MatcherAssert.assertThat;
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
*/
public class GeneratedValueTests extends BaseUnitTestCase {
@ -60,7 +65,9 @@ public class GeneratedValueTests extends BaseUnitTestCase {
@Test
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 )
.addAnnotatedClass( ImplicitSequenceGeneratorEntity.class )
.buildMetadata();
@ -81,9 +88,7 @@ public class GeneratedValueTests extends BaseUnitTestCase {
@Test
public void testImplicitSequenceGeneratorGeneratorName() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, "true" )
.build();
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ImplicitSequenceGeneratorEntity.class )
.buildMetadata();
@ -105,7 +110,9 @@ public class GeneratedValueTests extends BaseUnitTestCase {
@Test
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 )
.addAnnotatedClass( ExplicitSequenceGeneratorImplicitNameEntity.class )
.buildMetadata();
@ -127,9 +134,8 @@ public class GeneratedValueTests extends BaseUnitTestCase {
@Test
public void testExplicitSequenceGeneratorImplicitNamePreferGeneratorName() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, "true" )
.build();
// this should be the default behavior
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ExplicitSequenceGeneratorImplicitNameEntity.class )
.buildMetadata();
@ -171,6 +177,42 @@ public class GeneratedValueTests extends BaseUnitTestCase {
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
public static class ExplicitGeneratorEntity {
/**
@ -216,4 +258,27 @@ public class GeneratedValueTests extends BaseUnitTestCase {
public Integer id;
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;
}
}