From 1d61210826aa5277136a27b86d1fcd7b65dfa378 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 4 Aug 2015 17:00:46 -0500 Subject: [PATCH] HHH-9850 - Primary key generated for nullable column in sequence table --- .../id/MultipleHiLoPerTableGenerator.java | 6 + .../hibernate/id/enhanced/TableGenerator.java | 111 +++++++++++------- .../internal/log/DeprecationLogger.java | 9 ++ .../enhanced/table/Db2GenerationTest.java | 69 +++++++++++ 4 files changed, 150 insertions(+), 45 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/Db2GenerationTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java index 88cba0b445..4925e17b4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java @@ -33,6 +33,7 @@ import org.hibernate.id.enhanced.AccessCallback; import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.log.DeprecationLogger; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.jdbc.AbstractReturningWork; import org.hibernate.jdbc.WorkExecutorVisitable; @@ -73,7 +74,10 @@ import org.hibernate.type.Type; * * @author Emmanuel Bernard * @author Klaus Richarz. + * + * @deprecated Use {@link org.hibernate.id.enhanced.TableGenerator} instead. */ +@Deprecated public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenerator, Configurable { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( MultipleHiLoPerTableGenerator.class ); @@ -106,6 +110,8 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera private int keySize; public synchronized Serializable generate(final SessionImplementor session, Object obj) { + DeprecationLogger.DEPRECATION_LOGGER.deprecatedTableGenerator( getClass().getName() ); + final SqlStatementLogger statementLogger = session.getFactory().getServiceRegistry() .getService( JdbcServices.class ) .getSqlStatementLogger(); diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java index 7399c56b7b..25ad3919ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java @@ -20,7 +20,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MappingException; -import org.hibernate.boot.model.naming.ObjectNameNormalizer; +import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.Namespace; import org.hibernate.boot.model.relational.QualifiedName; @@ -351,10 +351,9 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); final Dialect dialect = jdbcEnvironment.getDialect(); - qualifiedTableName = determineGeneratorTableName( params, dialect ); - renderedTableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format( qualifiedTableName, dialect ); - segmentColumnName = determineSegmentColumnName( params, dialect ); - valueColumnName = determineValueColumnName( params, dialect ); + qualifiedTableName = determineGeneratorTableName( params, jdbcEnvironment ); + segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment ); + valueColumnName = determineValueColumnName( params, jdbcEnvironment ); segmentValue = determineSegmentValue( params ); @@ -362,10 +361,6 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab initialValue = determineInitialValue( params ); incrementSize = determineIncrementSize( params ); - this.selectQuery = buildSelectQuery( dialect ); - this.updateQuery = buildUpdateQuery(); - this.insertQuery = buildInsertQuery(); - // if the increment size is greater than one, we prefer pooled optimization; but we // need to see if the user prefers POOL or POOL_LO... final String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false ) @@ -390,18 +385,30 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab * * @see #getTableName() * @param params The params supplied in the generator config (plus some standard useful extras). - * @param dialect The dialect in effect + * @param jdbcEnvironment The JDBC environment * @return The table name to use. */ @SuppressWarnings("UnusedParameters") - protected QualifiedName determineGeneratorTableName(Properties params, Dialect dialect) { - final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); + protected QualifiedName determineGeneratorTableName(Properties params, JdbcEnvironment jdbcEnvironment) { + final String tableName = ConfigurationHelper.getString( TABLE_PARAM, params, DEF_TABLE ); - return QualifiedNameParser.INSTANCE.parse( - ConfigurationHelper.getString( TABLE_PARAM, params, DEF_TABLE ), - normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) ), - normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) ) - ); + 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 ) + ); + } } /** @@ -412,14 +419,13 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab * * @see #getSegmentColumnName() * @param params The params supplied in the generator config (plus some standard useful extras). - * @param dialect The dialect in effect + * @param jdbcEnvironment The JDBC environment * @return The name of the segment column */ @SuppressWarnings("UnusedParameters") - protected String determineSegmentColumnName(Properties params, Dialect dialect) { - final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); + protected String determineSegmentColumnName(Properties params, JdbcEnvironment jdbcEnvironment) { final String name = ConfigurationHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN ); - return normalizer.toDatabaseIdentifierText( name ); + return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() ); } /** @@ -429,14 +435,13 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab * * @see #getValueColumnName() * @param params The params supplied in the generator config (plus some standard useful extras). - * @param dialect The dialect in effect + * @param jdbcEnvironment The JDBC environment * @return The name of the value column */ @SuppressWarnings("UnusedParameters") - protected String determineValueColumnName(Properties params, Dialect dialect) { - final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); + protected String determineValueColumnName(Properties params, JdbcEnvironment jdbcEnvironment) { final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN ); - return normalizer.toDatabaseIdentifierText( name ); + return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() ); } /** @@ -667,29 +672,45 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab qualifiedTableName.getCatalogName(), qualifiedTableName.getSchemaName() ); - final Table table = namespace.createTable( qualifiedTableName.getObjectName(), false ); - final Column segmentColumn = new ExportableColumn( - database, - table, - segmentColumnName, - StringType.INSTANCE, - dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 ) + Table table = namespace.locateTable( qualifiedTableName.getObjectName() ); + if ( table == null ) { + table = namespace.createTable( qualifiedTableName.getObjectName(), false ); + + // todo : note sure the best solution here. do we add the columns if missing? other? + final Column segmentColumn = new ExportableColumn( + database, + table, + segmentColumnName, + StringType.INSTANCE, + dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 ) + ); + segmentColumn.setNullable( false ); + table.addColumn( segmentColumn ); + + // lol + table.setPrimaryKey( new PrimaryKey() ); + table.getPrimaryKey().setTable( table ); + table.getPrimaryKey().addColumn( segmentColumn ); + + final Column valueColumn = new ExportableColumn( + database, + table, + valueColumnName, + LongType.INSTANCE + ); + table.addColumn( valueColumn ); + } + + // allow physical naming strategies a chance to kick in + this.renderedTableName = database.getJdbcEnvironment().getQualifiedObjectNameFormatter().format( + table.getQualifiedTableName(), + dialect ); - segmentColumn.setNullable( false ); - table.addColumn( segmentColumn ); - // lol - table.setPrimaryKey( new PrimaryKey() ); - table.getPrimaryKey().setTable( table ); - table.getPrimaryKey().addColumn( segmentColumn ); + this.selectQuery = buildSelectQuery( dialect ); + this.updateQuery = buildUpdateQuery(); + this.insertQuery = buildInsertQuery(); - final Column valueColumn = new ExportableColumn( - database, - table, - valueColumnName, - LongType.INSTANCE - ); - table.addColumn( valueColumn ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java index 6ec7a36d01..1513b29dc0 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java @@ -168,4 +168,13 @@ public interface DeprecationLogger { "See Hibernate Domain Model Mapping Guide for details." ) void deprecatedSequenceGenerator(String generatorImpl); + + @LogMessage(level = WARN) + @Message( + id = 90000015, + value = "Found use of deprecated [%s] table-based id generator; " + + "use org.hibernate.id.enhanced.TableGenerator instead. " + + "See Hibernate Domain Model Mapping Guide for details." + ) + void deprecatedTableGenerator(String generatorImpl); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/Db2GenerationTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/Db2GenerationTest.java new file mode 100644 index 0000000000..cfd0871474 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/Db2GenerationTest.java @@ -0,0 +1,69 @@ +/* + * 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 . + */ +package org.hibernate.test.idgen.enhanced.table; + +import java.util.Properties; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.id.enhanced.TableGenerator; +import org.hibernate.mapping.Table; +import org.hibernate.type.IntegerType; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Steve Ebersole + */ +public class Db2GenerationTest extends BaseUnitTestCase { + @Test + @TestForIssue( jiraKey = "HHH-9850" ) + public void testDb2TableGeneratorCreation() { + StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() + .applySetting( AvailableSettings.DIALECT, DB2Dialect.class.getName() ) + .build(); + + try { + Metadata metadata = new MetadataSources( ssr ) + .buildMetadata(); + + assertEquals( 0, metadata.getDatabase().getDefaultNamespace().getTables().size() ); + + TableGenerator generator = new TableGenerator(); + + Properties properties = new Properties(); + generator.configure( IntegerType.INSTANCE, properties, ssr ); + + generator.registerExportables( metadata.getDatabase() ); + + assertEquals( 1, metadata.getDatabase().getDefaultNamespace().getTables().size() ); + + final Table table = metadata.getDatabase().getDefaultNamespace().getTables().iterator().next(); + final String[] createCommands = new DB2Dialect().getTableExporter().getSqlCreateStrings( table, metadata ); + assertContains( "sequence_name varchar(255) not null", createCommands[0] ); + } + finally { + StandardServiceRegistryBuilder.destroy( ssr ); + } + } + + private void assertContains(String subStr, String str) { + if ( !str.contains( subStr ) ) { + Assert.fail( "String [" + str + "] did not contain expected substring [" + subStr + "]" ); + } + } +}