HHH-15143 - Add an "implicit naming strategy" for database structures (sequence and tables) for identifier generators

This commit is contained in:
Andrea Boriero 2022-03-28 13:26:35 +02:00 committed by Steve Ebersole
parent 33e2faced2
commit 2709bc70b0
9 changed files with 682 additions and 37 deletions

View File

@ -23,6 +23,8 @@ import org.hibernate.service.ServiceRegistry;
*/
@Incubating
public interface ImplicitIdentifierDatabaseObjectNamingStrategy {
String DEF_SEQUENCE = "hibernate_sequence";
/**
* Determine the implicit name for an identifier-generator sequence
*

View File

@ -0,0 +1,59 @@
/*
* 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.id.enhanced;
import java.util.Map;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.spi.ImplicitIdentifierDatabaseObjectNamingStrategy;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.QualifiedNameParser;
import org.hibernate.boot.model.relational.QualifiedSequenceName;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.ServiceRegistry;
import static org.hibernate.id.enhanced.TableGenerator.DEF_TABLE;
import static org.hibernate.id.enhanced.TableGenerator.TABLE_PARAM;
public class LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy implements
ImplicitIdentifierDatabaseObjectNamingStrategy {
public QualifiedName determineSequenceName(
Identifier catalogName,
Identifier schemaName,
Map<?, ?> configValues,
ServiceRegistry serviceRegistry) {
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
return new QualifiedSequenceName(
catalogName,
schemaName,
jdbcEnvironment.getIdentifierHelper().toIdentifier( DEF_SEQUENCE )
);
}
public QualifiedName determineTableName(
Identifier catalogName,
Identifier schemaName,
Map<?, ?> configValues,
ServiceRegistry serviceRegistry) {
final String tableName = ConfigurationHelper.getString( TABLE_PARAM, configValues, DEF_TABLE );
if ( tableName.contains( "." ) ) {
return QualifiedNameParser.INSTANCE.parse( tableName );
}
else {
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
return new QualifiedNameParser.NameParts(
catalogName,
schemaName,
jdbcEnvironment.getIdentifierHelper().toIdentifier( tableName )
);
}
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.id.enhanced;
import java.util.Map;
import java.util.Objects;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.spi.ImplicitIdentifierDatabaseObjectNamingStrategy;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.QualifiedNameParser;
import org.hibernate.boot.model.relational.QualifiedSequenceName;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.ServiceRegistry;
import static org.hibernate.id.enhanced.SequenceStyleGenerator.CONFIG_SEQUENCE_PER_ENTITY_SUFFIX;
import static org.hibernate.id.enhanced.SequenceStyleGenerator.DEF_SEQUENCE_SUFFIX;
import static org.hibernate.id.enhanced.TableGenerator.DEF_TABLE;
import static org.hibernate.id.enhanced.TableGenerator.TABLE_PARAM;
public class LegacyPreferDefaultGeneratorNameDatabaseNamingStrategy implements
ImplicitIdentifierDatabaseObjectNamingStrategy {
public QualifiedName determineSequenceName(
Identifier catalogName,
Identifier schemaName,
Map<?, ?> configValues,
ServiceRegistry serviceRegistry) {
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
return new QualifiedSequenceName(
catalogName,
schemaName,
jdbcEnvironment.getIdentifierHelper().toIdentifier( implicitName( configValues ) )
);
}
private String implicitName(Map<?, ?> configValues) {
final String suffix = ConfigurationHelper.getString(
CONFIG_SEQUENCE_PER_ENTITY_SUFFIX,
configValues,
DEF_SEQUENCE_SUFFIX
);
if ( !Objects.equals( suffix, DEF_SEQUENCE_SUFFIX ) ) {
return DEF_SEQUENCE;
}
final String annotationGeneratorName = ConfigurationHelper.getString(
IdentifierGenerator.GENERATOR_NAME,
configValues
);
if ( StringHelper.isNotEmpty( annotationGeneratorName ) ) {
return annotationGeneratorName;
}
return DEF_SEQUENCE;
}
@Override
public QualifiedName determineTableName(
Identifier catalogName,
Identifier schemaName,
Map<?, ?> configValues,
ServiceRegistry serviceRegistry) {
String fallbackTableName = DEF_TABLE;
final String generatorName = ConfigurationHelper.getString( IdentifierGenerator.GENERATOR_NAME, configValues );
if ( StringHelper.isNotEmpty( generatorName ) ) {
fallbackTableName = generatorName;
}
final String tableName = ConfigurationHelper.getString( TABLE_PARAM, configValues, fallbackTableName );
if ( tableName.contains( "." ) ) {
return QualifiedNameParser.INSTANCE.parse( tableName );
}
else {
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
return new QualifiedNameParser.NameParts(
catalogName,
schemaName,
jdbcEnvironment.getIdentifierHelper().toIdentifier( tableName )
);
}
}
}

View File

@ -324,7 +324,6 @@ public class SequenceStyleGenerator
Properties params,
ServiceRegistry serviceRegistry) {
final StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class );
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
final String namingStrategySetting = coalesceSuppliedValues(
() -> {
@ -335,6 +334,7 @@ public class SequenceStyleGenerator
return localSetting;
},
() -> {
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
final String globalSetting = ConfigurationHelper.getString( ID_DB_STRUCTURE_NAMING_STRATEGY, configurationService.getSettings() );
if ( globalSetting != null ) {
INCUBATION_LOGGER.incubatingSetting( ID_DB_STRUCTURE_NAMING_STRATEGY );

View File

@ -13,8 +13,8 @@ import org.hibernate.MappingException;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.spi.ImplicitIdentifierDatabaseObjectNamingStrategy;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.QualifiedNameParser;
import org.hibernate.boot.model.relational.QualifiedSequenceName;
import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
@ -25,11 +25,15 @@ import org.hibernate.service.ServiceRegistry;
import static org.hibernate.id.OptimizableGenerator.IMPLICIT_NAME_BASE;
import static org.hibernate.id.enhanced.SequenceStyleGenerator.CONFIG_SEQUENCE_PER_ENTITY_SUFFIX;
import static org.hibernate.id.enhanced.SequenceStyleGenerator.DEF_SEQUENCE_SUFFIX;
import static org.hibernate.id.enhanced.TableGenerator.DEF_TABLE;
import static org.hibernate.id.enhanced.TableGenerator.TABLE_PARAM;
/**
* @author Steve Ebersole
*/
public class StandardImplicitIdentifierDatabaseObjectNamingStrategy implements ImplicitIdentifierDatabaseObjectNamingStrategy {
@Override
public QualifiedName determineSequenceName(
Identifier catalogName,
@ -52,7 +56,6 @@ public class StandardImplicitIdentifierDatabaseObjectNamingStrategy implements I
String rootTableName,
Map<?, ?> configValues,
ServiceRegistry serviceRegistry) {
final String annotationGeneratorName = ConfigurationHelper.getString( IdentifierGenerator.GENERATOR_NAME, configValues );
final String base = ConfigurationHelper.getString( IMPLICIT_NAME_BASE, configValues );
final String suffix = ConfigurationHelper.getString( CONFIG_SEQUENCE_PER_ENTITY_SUFFIX, configValues, DEF_SEQUENCE_SUFFIX );
@ -66,6 +69,7 @@ public class StandardImplicitIdentifierDatabaseObjectNamingStrategy implements I
}
}
final String annotationGeneratorName = ConfigurationHelper.getString( IdentifierGenerator.GENERATOR_NAME, configValues );
if ( StringHelper.isNotEmpty( annotationGeneratorName ) ) {
return annotationGeneratorName;
}
@ -80,22 +84,32 @@ public class StandardImplicitIdentifierDatabaseObjectNamingStrategy implements I
throw new MappingException( "Unable to determine implicit sequence name; target table - " + rootTableName );
}
@Override
public QualifiedName determineTableName(
Identifier catalogName,
Identifier schemaName,
Map<?, ?> configValues,
ServiceRegistry serviceRegistry) {
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
String fallbackTableName = DEF_TABLE;
final String rootTableName = ConfigurationHelper.getString( PersistentIdentifierGenerator.TABLE, configValues );
final String implicitName = implicitName( rootTableName, configValues, serviceRegistry );
final String generatorName = ConfigurationHelper.getString( IdentifierGenerator.GENERATOR_NAME, configValues );
if ( StringHelper.isNotEmpty( generatorName ) ) {
fallbackTableName = generatorName;
}
return new QualifiedTableName(
catalogName,
schemaName,
jdbcEnvironment.getIdentifierHelper().toIdentifier( implicitName )
);
final String tableName = ConfigurationHelper.getString( TABLE_PARAM, configValues, fallbackTableName );
if ( tableName.contains( "." ) ) {
return QualifiedNameParser.INSTANCE.parse( tableName );
}
else {
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
return new QualifiedNameParser.NameParts(
catalogName,
schemaName,
jdbcEnvironment.getIdentifierHelper().toIdentifier( tableName )
);
}
}
}

View File

@ -19,12 +19,13 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.spi.ImplicitIdentifierDatabaseObjectNamingStrategy;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.InitCommand;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.QualifiedNameParser;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService;
@ -37,7 +38,6 @@ import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.engine.spi.SessionEventListenerManager;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.ExportableColumn;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.id.PersistentIdentifierGenerator;
@ -55,6 +55,10 @@ import org.hibernate.type.Type;
import org.jboss.logging.Logger;
import static org.hibernate.cfg.AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY;
import static org.hibernate.internal.log.IncubationLogger.INCUBATION_LOGGER;
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
/**
* An enhanced version of table-based id generation.
* <p/>
@ -369,33 +373,40 @@ public class TableGenerator implements PersistentIdentifierGenerator {
* @return The table name to use.
*/
protected QualifiedName determineGeneratorTableName(Properties params, JdbcEnvironment jdbcEnvironment, ServiceRegistry serviceRegistry) {
final StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class );
String fallbackTableName = DEF_TABLE;
final String namingStrategySetting = coalesceSuppliedValues(
() -> {
final String localSetting = ConfigurationHelper.getString( ID_DB_STRUCTURE_NAMING_STRATEGY, params );
if ( localSetting != null ) {
INCUBATION_LOGGER.incubatingSetting( ID_DB_STRUCTURE_NAMING_STRATEGY );
}
return localSetting;
},
() -> {
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
final String globalSetting = ConfigurationHelper.getString( ID_DB_STRUCTURE_NAMING_STRATEGY, configurationService.getSettings() );
if ( globalSetting != null ) {
INCUBATION_LOGGER.incubatingSetting( ID_DB_STRUCTURE_NAMING_STRATEGY );
}
return globalSetting;
},
StandardImplicitIdentifierDatabaseObjectNamingStrategy.class::getName
);
final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME );
if ( StringHelper.isNotEmpty( generatorName ) ) {
fallbackTableName = generatorName;
}
final ImplicitIdentifierDatabaseObjectNamingStrategy namingStrategy = strategySelector.resolveStrategy(
ImplicitIdentifierDatabaseObjectNamingStrategy.class,
namingStrategySetting
);
String tableName = ConfigurationHelper.getString( TABLE_PARAM, params, fallbackTableName );
final Identifier catalog = jdbcEnvironment.getIdentifierHelper().toIdentifier(
ConfigurationHelper.getString( CATALOG, params )
);
final Identifier schema = jdbcEnvironment.getIdentifierHelper().toIdentifier(
ConfigurationHelper.getString( SCHEMA, params )
);
if ( tableName.contains( "." ) ) {
return QualifiedNameParser.INSTANCE.parse( tableName );
}
else {
// todo : need to incorporate implicit catalog and schema names
final Identifier catalog = jdbcEnvironment.getIdentifierHelper().toIdentifier(
ConfigurationHelper.getString( CATALOG, params )
);
final Identifier schema = jdbcEnvironment.getIdentifierHelper().toIdentifier(
ConfigurationHelper.getString( SCHEMA, params )
);
return new QualifiedNameParser.NameParts(
catalog,
schema,
jdbcEnvironment.getIdentifierHelper().toIdentifier( tableName )
);
}
return namingStrategy.determineTableName( catalog, schema, params, serviceRegistry );
}
/**

View File

@ -0,0 +1,258 @@
/*
* 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.schemaupdate.idgenerator;
import java.io.File;
import java.nio.file.Files;
import java.util.EnumSet;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.id.enhanced.LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy;
import org.hibernate.id.enhanced.LegacyPreferDefaultGeneratorNameDatabaseNamingStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType;
import org.hibernate.testing.ServiceRegistryBuilder;
import org.hibernate.testing.orm.junit.BaseUnitTest;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BaseUnitTest
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsSequences.class)
public class SequenceIdGenerationStrategyTest {
private File output;
private ServiceRegistry ssr;
private MetadataImplementor metadata;
@BeforeEach
public void setUp() throws Exception {
output = File.createTempFile( "update_script", ".sql" );
output.deleteOnExit();
}
private void buildMetadata(Class annotatedClass) {
buildMetadata( null, annotatedClass );
}
private void buildMetadata(String namingStrategy, Class annotatedClass) {
StandardServiceRegistryBuilder standardServiceRegistryBuilder = new StandardServiceRegistryBuilder();
standardServiceRegistryBuilder.applySetting( AvailableSettings.FORMAT_SQL, "false" );
if ( namingStrategy != null ) {
standardServiceRegistryBuilder.applySetting(
AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY,
namingStrategy
);
}
ssr = standardServiceRegistryBuilder.build();
final MetadataSources metadataSources = new MetadataSources( ssr );
metadataSources.addAnnotatedClass( annotatedClass );
metadata = (MetadataImplementor) metadataSources.buildMetadata();
metadata.validate();
}
@AfterEach
public void tearDown() {
new SchemaExport().drop( EnumSet.of( TargetType.DATABASE ), metadata );
ServiceRegistryBuilder.destroy( ssr );
}
@Test
public void testSequenceNameStandardStrategy() throws Exception {
buildMetadata( TestEntity.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "TestEntity_SEQ" ) );
}
@Test
public void testSequenceNameHibernateSequenceStrategy() throws Exception {
buildMetadata( LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(), TestEntity.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "hibernate_sequence" ) );
}
@Test
public void testSequenceNamePreferGeneratorNameStrategy() throws Exception {
buildMetadata(
LegacyPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(),
TestEntity.class
);
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "hibernate_sequence" ) );
}
@Test
public void testNoGeneratorStandardStrategy() throws Exception {
buildMetadata( TestEntity2.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "table_generator" ) );
}
@Test
public void testNoGeneratorHibernateSequenceStrategy() throws Exception {
buildMetadata( LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(), TestEntity2.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "hibernate_sequence" ) );
}
@Test
public void testNoGeneratorPreferGeneratorNameStrategy() throws Exception {
buildMetadata(
LegacyPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(),
TestEntity2.class
);
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "table_generator" ) );
}
@Test
public void testGeneratorWithoutSequenceNameStandardStrategy() throws Exception {
buildMetadata( TestEntity3.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "table_generator" ) );
}
@Test
public void testGeneratorWithoutSequenceNameHibernateSequenceStrategy() throws Exception {
buildMetadata( LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(), TestEntity3.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "hibernate_sequence" ) );
}
@Test
public void testGeneratorWithoutSequenceNamePreferGeneratorNameStrategy() throws Exception {
buildMetadata(
LegacyPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(),
TestEntity3.class
);
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "table_generator" ) );
}
@Test
public void testGeneratorWithSequenceNameStandardStrategy() throws Exception {
buildMetadata( TestEntity4.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "test_sequence" ) );
}
@Test
public void testGeneratorWithSequenceNameHibernateSequenceStrategy() throws Exception {
buildMetadata( LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(), TestEntity4.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "test_sequence" ) );
}
@Test
public void testGeneratorWithSequenceNamePreferGeneratorNameStrategy() throws Exception {
buildMetadata(
LegacyPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(),
TestEntity4.class
);
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "test_sequence" ) );
}
private void createSchema() {
final SchemaExport schemaExport = new SchemaExport();
schemaExport.setOutputFile( output.getAbsolutePath() );
schemaExport.create( EnumSet.of( TargetType.DATABASE, TargetType.SCRIPT ), metadata );
}
@Entity(name = "TestEntity")
public static class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
}
@Entity(name = "TestEntity2")
public static class TestEntity2 {
@Id
@GeneratedValue(generator = "table_generator")
private Long id;
private String name;
}
@Entity(name = "TestEntity3")
public static class TestEntity3 {
@Id
@GeneratedValue(generator = "table_generator")
@SequenceGenerator(name = "table_generator")
private Long id;
private String name;
}
@Entity(name = "TestEntity4")
public static class TestEntity4 {
@Id
@GeneratedValue(generator = "table_generator")
@SequenceGenerator(name = "table_generator", sequenceName = "test_sequence")
private Long id;
private String name;
}
}

View File

@ -0,0 +1,191 @@
/*
* 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.schemaupdate.idgenerator;
import java.io.File;
import java.nio.file.Files;
import java.util.EnumSet;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.id.enhanced.LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType;
import org.hibernate.testing.ServiceRegistryBuilder;
import org.hibernate.testing.orm.junit.BaseUnitTest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.TableGenerator;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BaseUnitTest
public class TableGenerationStrategyTest {
private File output;
private ServiceRegistry ssr;
private MetadataImplementor metadata;
@BeforeEach
public void setUp() throws Exception {
output = File.createTempFile( "update_script", ".sql" );
output.deleteOnExit();
}
@AfterEach
public void tearDown() {
new SchemaExport().drop( EnumSet.of( TargetType.DATABASE ), metadata );
ServiceRegistryBuilder.destroy( ssr );
}
private void buildMetadata(Class annotatedClass) {
buildMetadata( null, annotatedClass );
}
private void buildMetadata(String namingStrategy, Class annotatedClass) {
StandardServiceRegistryBuilder standardServiceRegistryBuilder = new StandardServiceRegistryBuilder();
standardServiceRegistryBuilder.applySetting( AvailableSettings.FORMAT_SQL, "false" );
if ( namingStrategy != null ) {
standardServiceRegistryBuilder.applySetting(
AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY,
namingStrategy
);
}
ssr = standardServiceRegistryBuilder.build();
final MetadataSources metadataSources = new MetadataSources( ssr );
metadataSources.addAnnotatedClass( annotatedClass );
metadata = (MetadataImplementor) metadataSources.buildMetadata();
metadata.validate();
}
private void createSchema() {
final SchemaExport schemaExport = new SchemaExport();
schemaExport.setOutputFile( output.getAbsolutePath() );
schemaExport.create( EnumSet.of( TargetType.DATABASE, TargetType.SCRIPT ), metadata );
}
@Test
public void testTableNameStandardStrategy() throws Exception {
buildMetadata( TestEntity.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "hibernate_sequences" ) );
}
@Test
public void testNoGeneratorStandardStrategy() throws Exception {
buildMetadata( TestEntity2.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "table_generator" ) );
}
@Test
public void testNoGeneratorNoPreferGeneratorNameStrategy() throws Exception {
buildMetadata( LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(), TestEntity2.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "hibernate_sequence" ) );
}
@Test
public void testGeneratorWithoutSequenceNameStandardStrategy() throws Exception {
buildMetadata( TestEntity3.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "table_generator" ) );
}
@Test
public void testGeneratorWithoutSequenceNameStandardStrategyNoPreferGeneratorNameStrategy() throws Exception {
buildMetadata( LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(), TestEntity3.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "hibernate_sequences" ) );
}
@Test
public void testGeneratorWithSequenceNameStandardStrategy() throws Exception {
buildMetadata( TestEntity4.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "test_table" ) );
}
@Test
public void testGeneratorWithSequenceNameNoPreferGeneratorNameStrategy() throws Exception {
buildMetadata( LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy.class.getName(), TestEntity4.class );
createSchema();
final String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertTrue( fileContent.contains( "test_table" ) );
}
@Entity(name = "TestEntity")
public static class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
private String name;
}
@Entity(name = "TestEntity2")
public static class TestEntity2 {
@Id
@GeneratedValue(generator = "table_generator")
private Long id;
private String name;
}
@Entity(name = "TestEntity3")
public static class TestEntity3 {
@Id
@GeneratedValue(generator = "table_generator")
@TableGenerator(name = "table_generator")
private Long id;
private String name;
}
@Entity(name = "TestEntity4")
public static class TestEntity4 {
@Id
@GeneratedValue(generator = "table_generator")
@TableGenerator(name = "table_generator", table = "test_table")
private Long id;
private String name;
}
}

View File

@ -99,6 +99,22 @@ to define custom generators to use for identifier generation.
// todo (6.0 - @Steve - need to add content about this to the User Guide
[[id-sequence-generation]]
== Id Sequence and Table generation
For a sequence id generation strategy Hibernate 6.0 by default does not create anymore a single sequence named `hibernate_sequence` but it creates one sequence for each entity.
In order to maintain backward compatibility a new setting `hibernate.id.db_structure_naming_strategy`
has been introduced.
In case the code has a generator name with no matching generator or with a generator not specifying a sequence or table name
and the property `hibernate.model.generator_name_as_sequence_name` is not set to `false` then to maintain the backward compatibility is necessary to set
hibernate.id.db_structure_naming_strategy=org.hibernate.id.enhanced.LegacyPreferDefaultGeneratorNameDatabaseNamingStrategy
```
otherwise
```
hibernate.id.db_structure_naming_strategy=org.hibernate.id.enhanced.LegacyNoPreferDefaultGeneratorNameDatabaseNamingStrategy
```
[[type]]
== Type system