Skip to content

Commit 1d61210

Browse files
committed
HHH-9850 - Primary key generated for nullable column in sequence table
1 parent 54a3bb2 commit 1d61210

File tree

4 files changed

+153
-48
lines changed

4 files changed

+153
-48
lines changed

hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer;
3434
import org.hibernate.internal.CoreLogging;
3535
import org.hibernate.internal.CoreMessageLogger;
36+
import org.hibernate.internal.log.DeprecationLogger;
3637
import org.hibernate.internal.util.config.ConfigurationHelper;
3738
import org.hibernate.jdbc.AbstractReturningWork;
3839
import org.hibernate.jdbc.WorkExecutorVisitable;
@@ -73,7 +74,10 @@
7374
*
7475
* @author Emmanuel Bernard
7576
* @author <a href="mailto:[email protected]">Klaus Richarz</a>.
77+
*
78+
* @deprecated Use {@link org.hibernate.id.enhanced.TableGenerator} instead.
7679
*/
80+
@Deprecated
7781
public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenerator, Configurable {
7882
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( MultipleHiLoPerTableGenerator.class );
7983

@@ -106,6 +110,8 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera
106110
private int keySize;
107111

108112
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
113+
DeprecationLogger.DEPRECATION_LOGGER.deprecatedTableGenerator( getClass().getName() );
114+
109115
final SqlStatementLogger statementLogger = session.getFactory().getServiceRegistry()
110116
.getService( JdbcServices.class )
111117
.getSqlStatementLogger();

hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import org.hibernate.LockMode;
2121
import org.hibernate.LockOptions;
2222
import org.hibernate.MappingException;
23-
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
23+
import org.hibernate.boot.model.naming.Identifier;
2424
import org.hibernate.boot.model.relational.Database;
2525
import org.hibernate.boot.model.relational.Namespace;
2626
import org.hibernate.boot.model.relational.QualifiedName;
@@ -351,21 +351,16 @@ public void configure(Type type, Properties params, ServiceRegistry serviceRegis
351351
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
352352
final Dialect dialect = jdbcEnvironment.getDialect();
353353

354-
qualifiedTableName = determineGeneratorTableName( params, dialect );
355-
renderedTableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format( qualifiedTableName, dialect );
356-
segmentColumnName = determineSegmentColumnName( params, dialect );
357-
valueColumnName = determineValueColumnName( params, dialect );
354+
qualifiedTableName = determineGeneratorTableName( params, jdbcEnvironment );
355+
segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment );
356+
valueColumnName = determineValueColumnName( params, jdbcEnvironment );
358357

359358
segmentValue = determineSegmentValue( params );
360359

361360
segmentValueLength = determineSegmentColumnSize( params );
362361
initialValue = determineInitialValue( params );
363362
incrementSize = determineIncrementSize( params );
364363

365-
this.selectQuery = buildSelectQuery( dialect );
366-
this.updateQuery = buildUpdateQuery();
367-
this.insertQuery = buildInsertQuery();
368-
369364
// if the increment size is greater than one, we prefer pooled optimization; but we
370365
// need to see if the user prefers POOL or POOL_LO...
371366
final String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false )
@@ -390,18 +385,30 @@ public void configure(Type type, Properties params, ServiceRegistry serviceRegis
390385
*
391386
* @see #getTableName()
392387
* @param params The params supplied in the generator config (plus some standard useful extras).
393-
* @param dialect The dialect in effect
388+
* @param jdbcEnvironment The JDBC environment
394389
* @return The table name to use.
395390
*/
396391
@SuppressWarnings("UnusedParameters")
397-
protected QualifiedName determineGeneratorTableName(Properties params, Dialect dialect) {
398-
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
392+
protected QualifiedName determineGeneratorTableName(Properties params, JdbcEnvironment jdbcEnvironment) {
393+
final String tableName = ConfigurationHelper.getString( TABLE_PARAM, params, DEF_TABLE );
399394

400-
return QualifiedNameParser.INSTANCE.parse(
401-
ConfigurationHelper.getString( TABLE_PARAM, params, DEF_TABLE ),
402-
normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) ),
403-
normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) )
404-
);
395+
if ( tableName.contains( "." ) ) {
396+
return QualifiedNameParser.INSTANCE.parse( tableName );
397+
}
398+
else {
399+
// todo : need to incorporate implicit catalog and schema names
400+
final Identifier catalog = jdbcEnvironment.getIdentifierHelper().toIdentifier(
401+
ConfigurationHelper.getString( CATALOG, params )
402+
);
403+
final Identifier schema = jdbcEnvironment.getIdentifierHelper().toIdentifier(
404+
ConfigurationHelper.getString( SCHEMA, params )
405+
);
406+
return new QualifiedNameParser.NameParts(
407+
catalog,
408+
schema,
409+
jdbcEnvironment.getIdentifierHelper().toIdentifier( tableName )
410+
);
411+
}
405412
}
406413

407414
/**
@@ -412,14 +419,13 @@ protected QualifiedName determineGeneratorTableName(Properties params, Dialect d
412419
*
413420
* @see #getSegmentColumnName()
414421
* @param params The params supplied in the generator config (plus some standard useful extras).
415-
* @param dialect The dialect in effect
422+
* @param jdbcEnvironment The JDBC environment
416423
* @return The name of the segment column
417424
*/
418425
@SuppressWarnings("UnusedParameters")
419-
protected String determineSegmentColumnName(Properties params, Dialect dialect) {
420-
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
426+
protected String determineSegmentColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
421427
final String name = ConfigurationHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
422-
return normalizer.toDatabaseIdentifierText( name );
428+
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() );
423429
}
424430

425431
/**
@@ -429,14 +435,13 @@ protected String determineSegmentColumnName(Properties params, Dialect dialect)
429435
*
430436
* @see #getValueColumnName()
431437
* @param params The params supplied in the generator config (plus some standard useful extras).
432-
* @param dialect The dialect in effect
438+
* @param jdbcEnvironment The JDBC environment
433439
* @return The name of the value column
434440
*/
435441
@SuppressWarnings("UnusedParameters")
436-
protected String determineValueColumnName(Properties params, Dialect dialect) {
437-
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
442+
protected String determineValueColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
438443
final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
439-
return normalizer.toDatabaseIdentifierText( name );
444+
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() );
440445
}
441446

442447
/**
@@ -667,29 +672,45 @@ public void registerExportables(Database database) {
667672
qualifiedTableName.getCatalogName(),
668673
qualifiedTableName.getSchemaName()
669674
);
670-
final Table table = namespace.createTable( qualifiedTableName.getObjectName(), false );
671-
672-
final Column segmentColumn = new ExportableColumn(
673-
database,
674-
table,
675-
segmentColumnName,
676-
StringType.INSTANCE,
677-
dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 )
678-
);
679-
segmentColumn.setNullable( false );
680-
table.addColumn( segmentColumn );
681-
682-
// lol
683-
table.setPrimaryKey( new PrimaryKey() );
684-
table.getPrimaryKey().setTable( table );
685-
table.getPrimaryKey().addColumn( segmentColumn );
686-
687-
final Column valueColumn = new ExportableColumn(
688-
database,
689-
table,
690-
valueColumnName,
691-
LongType.INSTANCE
675+
676+
Table table = namespace.locateTable( qualifiedTableName.getObjectName() );
677+
if ( table == null ) {
678+
table = namespace.createTable( qualifiedTableName.getObjectName(), false );
679+
680+
// todo : note sure the best solution here. do we add the columns if missing? other?
681+
final Column segmentColumn = new ExportableColumn(
682+
database,
683+
table,
684+
segmentColumnName,
685+
StringType.INSTANCE,
686+
dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 )
687+
);
688+
segmentColumn.setNullable( false );
689+
table.addColumn( segmentColumn );
690+
691+
// lol
692+
table.setPrimaryKey( new PrimaryKey() );
693+
table.getPrimaryKey().setTable( table );
694+
table.getPrimaryKey().addColumn( segmentColumn );
695+
696+
final Column valueColumn = new ExportableColumn(
697+
database,
698+
table,
699+
valueColumnName,
700+
LongType.INSTANCE
701+
);
702+
table.addColumn( valueColumn );
703+
}
704+
705+
// allow physical naming strategies a chance to kick in
706+
this.renderedTableName = database.getJdbcEnvironment().getQualifiedObjectNameFormatter().format(
707+
table.getQualifiedTableName(),
708+
dialect
692709
);
693-
table.addColumn( valueColumn );
710+
711+
this.selectQuery = buildSelectQuery( dialect );
712+
this.updateQuery = buildUpdateQuery();
713+
this.insertQuery = buildInsertQuery();
714+
694715
}
695716
}

hibernate-core/src/main/java/org/hibernate/internal/log/DeprecationLogger.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,13 @@ void connectionProviderClassDeprecated(
168168
"See Hibernate Domain Model Mapping Guide for details."
169169
)
170170
void deprecatedSequenceGenerator(String generatorImpl);
171+
172+
@LogMessage(level = WARN)
173+
@Message(
174+
id = 90000015,
175+
value = "Found use of deprecated [%s] table-based id generator; " +
176+
"use org.hibernate.id.enhanced.TableGenerator instead. " +
177+
"See Hibernate Domain Model Mapping Guide for details."
178+
)
179+
void deprecatedTableGenerator(String generatorImpl);
171180
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.test.idgen.enhanced.table;
8+
9+
import java.util.Properties;
10+
11+
import org.hibernate.boot.Metadata;
12+
import org.hibernate.boot.MetadataSources;
13+
import org.hibernate.boot.registry.StandardServiceRegistry;
14+
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
15+
import org.hibernate.cfg.AvailableSettings;
16+
import org.hibernate.dialect.DB2Dialect;
17+
import org.hibernate.id.enhanced.TableGenerator;
18+
import org.hibernate.mapping.Table;
19+
import org.hibernate.type.IntegerType;
20+
21+
import org.hibernate.testing.TestForIssue;
22+
import org.hibernate.testing.junit4.BaseUnitTestCase;
23+
import org.junit.Assert;
24+
import org.junit.Test;
25+
26+
import static org.junit.Assert.assertEquals;
27+
import static org.junit.Assert.assertTrue;
28+
29+
/**
30+
* @author Steve Ebersole
31+
*/
32+
public class Db2GenerationTest extends BaseUnitTestCase {
33+
@Test
34+
@TestForIssue( jiraKey = "HHH-9850" )
35+
public void testDb2TableGeneratorCreation() {
36+
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
37+
.applySetting( AvailableSettings.DIALECT, DB2Dialect.class.getName() )
38+
.build();
39+
40+
try {
41+
Metadata metadata = new MetadataSources( ssr )
42+
.buildMetadata();
43+
44+
assertEquals( 0, metadata.getDatabase().getDefaultNamespace().getTables().size() );
45+
46+
TableGenerator generator = new TableGenerator();
47+
48+
Properties properties = new Properties();
49+
generator.configure( IntegerType.INSTANCE, properties, ssr );
50+
51+
generator.registerExportables( metadata.getDatabase() );
52+
53+
assertEquals( 1, metadata.getDatabase().getDefaultNamespace().getTables().size() );
54+
55+
final Table table = metadata.getDatabase().getDefaultNamespace().getTables().iterator().next();
56+
final String[] createCommands = new DB2Dialect().getTableExporter().getSqlCreateStrings( table, metadata );
57+
assertContains( "sequence_name varchar(255) not null", createCommands[0] );
58+
}
59+
finally {
60+
StandardServiceRegistryBuilder.destroy( ssr );
61+
}
62+
}
63+
64+
private void assertContains(String subStr, String str) {
65+
if ( !str.contains( subStr ) ) {
66+
Assert.fail( "String [" + str + "] did not contain expected substring [" + subStr + "]" );
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)