From 1556c272d5b4d6e46762566f79694789725ba115 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 5 Aug 2015 09:09:07 -0500 Subject: [PATCH] HHH-9850 - Primary key generated for nullable column in sequence table --- .../id/MultipleHiLoPerTableGenerator.java | 161 +++++++++++------- .../hibernate/id/enhanced/TableGenerator.java | 1 - .../enhanced/table/Db2GenerationTest.java | 34 +++- 3 files changed, 131 insertions(+), 65 deletions(-) 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 4925e17b4f..ea582f89f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java @@ -17,7 +17,7 @@ import java.util.Properties; import org.hibernate.HibernateException; import org.hibernate.LockMode; 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; @@ -94,7 +94,8 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera private QualifiedName qualifiedTableName; private String tableName; - private String pkColumnName; + private String segmentColumnName; + private String segmentName; private String valueColumnName; private String query; private String insert; @@ -254,62 +255,58 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera @SuppressWarnings({"StatementWithEmptyBody", "deprecation"}) public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException { + returnClass = type.getReturnedClass(); + final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); - final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); - qualifiedTableName = QualifiedNameParser.INSTANCE.parse( - ConfigurationHelper.getString( ID_TABLE, params, DEFAULT_TABLE ), - normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) ), - normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) ) - ); - - tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format( - qualifiedTableName, - jdbcEnvironment.getDialect() - ); - pkColumnName = normalizer.toDatabaseIdentifierText( - ConfigurationHelper.getString( PK_COLUMN_NAME, params, DEFAULT_PK_COLUMN ) - ); - valueColumnName = normalizer.toDatabaseIdentifierText( - ConfigurationHelper.getString( VALUE_COLUMN_NAME, params, DEFAULT_VALUE_COLUMN ) - ); + qualifiedTableName = determineGeneratorTableName( params, jdbcEnvironment ); + segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment ); keySize = ConfigurationHelper.getInt( PK_LENGTH_NAME, params, DEFAULT_PK_LENGTH ); - String keyValue = ConfigurationHelper.getString( PK_VALUE_NAME, params, params.getProperty( TABLE ) ); - - query = "select " + - valueColumnName + - " from " + - jdbcEnvironment.getDialect().appendLockHint( LockMode.PESSIMISTIC_WRITE, tableName ) + - " where " + pkColumnName + " = '" + keyValue + "'" + - jdbcEnvironment.getDialect().getForUpdateString(); - - update = "update " + - tableName + - " set " + - valueColumnName + - " = ? where " + - valueColumnName + - " = ? and " + - pkColumnName + - " = '" + - keyValue - + "'"; - - insert = "insert into " + tableName + - "(" + pkColumnName + ", " + valueColumnName + ") " + - "values('" + keyValue + "', ?)"; + segmentName = ConfigurationHelper.getString( PK_VALUE_NAME, params, params.getProperty( TABLE ) ); + valueColumnName = determineValueColumnName( params, jdbcEnvironment ); //hilo config maxLo = ConfigurationHelper.getInt( MAX_LO, params, Short.MAX_VALUE ); - returnClass = type.getReturnedClass(); if ( maxLo >= 1 ) { hiloOptimizer = new LegacyHiLoAlgorithmOptimizer( returnClass, maxLo ); } } + protected QualifiedName determineGeneratorTableName(Properties params, JdbcEnvironment jdbcEnvironment) { + final String tableName = ConfigurationHelper.getString( ID_TABLE, params, DEFAULT_TABLE ); + + 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 ) + ); + } + } + + protected String determineSegmentColumnName(Properties params, JdbcEnvironment jdbcEnvironment) { + final String name = ConfigurationHelper.getString( PK_COLUMN_NAME, params, DEFAULT_PK_COLUMN ); + return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() ); + } + + protected String determineValueColumnName(Properties params, JdbcEnvironment jdbcEnvironment) { + final String name = ConfigurationHelper.getString( VALUE_COLUMN_NAME, params, DEFAULT_VALUE_COLUMN ); + return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() ); + } + @Override public void registerExportables(Database database) { final Namespace namespace = database.locateNamespace( @@ -317,33 +314,73 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera qualifiedTableName.getSchemaName() ); - final Table table = namespace.createTable( qualifiedTableName.getObjectName(), false ); - table.setPrimaryKey( new PrimaryKey() ); + Table table = namespace.locateTable( qualifiedTableName.getObjectName() ); + if ( table == null ) { + table = namespace.createTable( qualifiedTableName.getObjectName(), false ); - final Column pkColumn = new ExportableColumn( - database, - table, - pkColumnName, - StringType.INSTANCE, - database.getDialect().getTypeName( Types.VARCHAR, keySize, 0, 0 ) - ); - table.addColumn( pkColumn ); - table.getPrimaryKey().addColumn( pkColumn ); + // todo : note sure the best solution here. do we add the columns if missing? other? + table.setPrimaryKey( new PrimaryKey() ); - final Column valueColumn = new ExportableColumn( - database, - table, - valueColumnName, - LongType.INSTANCE + final Column pkColumn = new ExportableColumn( + database, + table, + segmentColumnName, + StringType.INSTANCE, + database.getDialect().getTypeName( Types.VARCHAR, keySize, 0, 0 ) + ); + pkColumn.setNullable( false ); + table.addColumn( pkColumn ); + table.getPrimaryKey().addColumn( pkColumn ); + + final Column valueColumn = new ExportableColumn( + database, + table, + valueColumnName, + LongType.INSTANCE + ); + table.addColumn( valueColumn ); + } + + final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment(); + + // allow physical naming strategies a chance to kick in + tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format( + table.getQualifiedTableName(), + jdbcEnvironment.getDialect() ); - table.addColumn( valueColumn ); + + query = "select " + + valueColumnName + + " from " + + jdbcEnvironment.getDialect().appendLockHint( LockMode.PESSIMISTIC_WRITE, tableName ) + + " where " + segmentColumnName + " = '" + segmentName + "'" + + jdbcEnvironment.getDialect().getForUpdateString(); + + update = "update " + + tableName + + " set " + + valueColumnName + + " = ? where " + + valueColumnName + + " = ? and " + + segmentColumnName + + " = '" + + segmentName + + "'"; + + insert = "insert into " + tableName + + "(" + segmentColumnName + ", " + valueColumnName + ") " + + "values('" + segmentName + "', ?)"; + + + } public String[] sqlCreateStrings(Dialect dialect) throws HibernateException { return new String[] { dialect.getCreateTableString() + ' ' + tableName + " ( " - + pkColumnName + ' ' + dialect.getTypeName( Types.VARCHAR, keySize, 0, 0 ) + ", " + + segmentColumnName + ' ' + dialect.getTypeName( Types.VARCHAR, keySize, 0, 0 ) + ", " + valueColumnName + ' ' + dialect.getTypeName( Types.INTEGER ) + " )" + dialect.getTableTypeString() }; 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 25ad3919ab..26aff0b818 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 @@ -349,7 +349,6 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab identifierType = type; final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); - final Dialect dialect = jdbcEnvironment.getDialect(); qualifiedTableName = determineGeneratorTableName( params, jdbcEnvironment ); segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment ); 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 index cfd0871474..cafc49a111 100644 --- 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 @@ -14,6 +14,7 @@ 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.MultipleHiLoPerTableGenerator; import org.hibernate.id.enhanced.TableGenerator; import org.hibernate.mapping.Table; import org.hibernate.type.IntegerType; @@ -24,7 +25,6 @@ import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; /** * @author Steve Ebersole @@ -32,7 +32,7 @@ import static org.junit.Assert.assertTrue; public class Db2GenerationTest extends BaseUnitTestCase { @Test @TestForIssue( jiraKey = "HHH-9850" ) - public void testDb2TableGeneratorCreation() { + public void testNewGeneratorTableCreationOnDb2() { StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, DB2Dialect.class.getName() ) .build(); @@ -66,4 +66,34 @@ public class Db2GenerationTest extends BaseUnitTestCase { Assert.fail( "String [" + str + "] did not contain expected substring [" + subStr + "]" ); } } + @Test + @TestForIssue( jiraKey = "HHH-9850" ) + public void testLegacyGeneratorTableCreationOnDb2() { + 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() ); + + MultipleHiLoPerTableGenerator generator = new MultipleHiLoPerTableGenerator(); + + 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 ); + } + } }