Skip to content

Commit 1556c27

Browse files
committed
HHH-9850 - Primary key generated for nullable column in sequence table
1 parent 182146f commit 1556c27

File tree

3 files changed

+127
-61
lines changed

3 files changed

+127
-61
lines changed

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

Lines changed: 95 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import org.hibernate.HibernateException;
1818
import org.hibernate.LockMode;
1919
import org.hibernate.MappingException;
20-
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
20+
import org.hibernate.boot.model.naming.Identifier;
2121
import org.hibernate.boot.model.relational.Database;
2222
import org.hibernate.boot.model.relational.Namespace;
2323
import org.hibernate.boot.model.relational.QualifiedName;
@@ -94,7 +94,8 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera
9494

9595
private QualifiedName qualifiedTableName;
9696
private String tableName;
97-
private String pkColumnName;
97+
private String segmentColumnName;
98+
private String segmentName;
9899
private String valueColumnName;
99100
private String query;
100101
private String insert;
@@ -254,34 +255,105 @@ private ResultSet executeQuery(PreparedStatement ps, SessionEventListenerManager
254255

255256
@SuppressWarnings({"StatementWithEmptyBody", "deprecation"})
256257
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
258+
returnClass = type.getReturnedClass();
259+
257260
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
258-
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
259261

260-
qualifiedTableName = QualifiedNameParser.INSTANCE.parse(
261-
ConfigurationHelper.getString( ID_TABLE, params, DEFAULT_TABLE ),
262-
normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) ),
263-
normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) )
262+
qualifiedTableName = determineGeneratorTableName( params, jdbcEnvironment );
263+
264+
segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment );
265+
keySize = ConfigurationHelper.getInt( PK_LENGTH_NAME, params, DEFAULT_PK_LENGTH );
266+
segmentName = ConfigurationHelper.getString( PK_VALUE_NAME, params, params.getProperty( TABLE ) );
267+
268+
valueColumnName = determineValueColumnName( params, jdbcEnvironment );
269+
270+
//hilo config
271+
maxLo = ConfigurationHelper.getInt( MAX_LO, params, Short.MAX_VALUE );
272+
273+
if ( maxLo >= 1 ) {
274+
hiloOptimizer = new LegacyHiLoAlgorithmOptimizer( returnClass, maxLo );
275+
}
276+
}
277+
278+
protected QualifiedName determineGeneratorTableName(Properties params, JdbcEnvironment jdbcEnvironment) {
279+
final String tableName = ConfigurationHelper.getString( ID_TABLE, params, DEFAULT_TABLE );
280+
281+
if ( tableName.contains( "." ) ) {
282+
return QualifiedNameParser.INSTANCE.parse( tableName );
283+
}
284+
else {
285+
// todo : need to incorporate implicit catalog and schema names
286+
final Identifier catalog = jdbcEnvironment.getIdentifierHelper().toIdentifier(
287+
ConfigurationHelper.getString( CATALOG, params )
288+
);
289+
final Identifier schema = jdbcEnvironment.getIdentifierHelper().toIdentifier(
290+
ConfigurationHelper.getString( SCHEMA, params )
291+
);
292+
return new QualifiedNameParser.NameParts(
293+
catalog,
294+
schema,
295+
jdbcEnvironment.getIdentifierHelper().toIdentifier( tableName )
296+
);
297+
}
298+
}
299+
300+
protected String determineSegmentColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
301+
final String name = ConfigurationHelper.getString( PK_COLUMN_NAME, params, DEFAULT_PK_COLUMN );
302+
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() );
303+
}
304+
305+
protected String determineValueColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
306+
final String name = ConfigurationHelper.getString( VALUE_COLUMN_NAME, params, DEFAULT_VALUE_COLUMN );
307+
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() );
308+
}
309+
310+
@Override
311+
public void registerExportables(Database database) {
312+
final Namespace namespace = database.locateNamespace(
313+
qualifiedTableName.getCatalogName(),
314+
qualifiedTableName.getSchemaName()
264315
);
265316

317+
Table table = namespace.locateTable( qualifiedTableName.getObjectName() );
318+
if ( table == null ) {
319+
table = namespace.createTable( qualifiedTableName.getObjectName(), false );
320+
321+
// todo : note sure the best solution here. do we add the columns if missing? other?
322+
table.setPrimaryKey( new PrimaryKey() );
323+
324+
final Column pkColumn = new ExportableColumn(
325+
database,
326+
table,
327+
segmentColumnName,
328+
StringType.INSTANCE,
329+
database.getDialect().getTypeName( Types.VARCHAR, keySize, 0, 0 )
330+
);
331+
pkColumn.setNullable( false );
332+
table.addColumn( pkColumn );
333+
table.getPrimaryKey().addColumn( pkColumn );
334+
335+
final Column valueColumn = new ExportableColumn(
336+
database,
337+
table,
338+
valueColumnName,
339+
LongType.INSTANCE
340+
);
341+
table.addColumn( valueColumn );
342+
}
343+
344+
final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment();
345+
346+
// allow physical naming strategies a chance to kick in
266347
tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
267-
qualifiedTableName,
348+
table.getQualifiedTableName(),
268349
jdbcEnvironment.getDialect()
269350
);
270-
pkColumnName = normalizer.toDatabaseIdentifierText(
271-
ConfigurationHelper.getString( PK_COLUMN_NAME, params, DEFAULT_PK_COLUMN )
272-
);
273-
valueColumnName = normalizer.toDatabaseIdentifierText(
274-
ConfigurationHelper.getString( VALUE_COLUMN_NAME, params, DEFAULT_VALUE_COLUMN )
275-
);
276-
277-
keySize = ConfigurationHelper.getInt( PK_LENGTH_NAME, params, DEFAULT_PK_LENGTH );
278-
String keyValue = ConfigurationHelper.getString( PK_VALUE_NAME, params, params.getProperty( TABLE ) );
279351

280352
query = "select " +
281353
valueColumnName +
282354
" from " +
283355
jdbcEnvironment.getDialect().appendLockHint( LockMode.PESSIMISTIC_WRITE, tableName ) +
284-
" where " + pkColumnName + " = '" + keyValue + "'" +
356+
" where " + segmentColumnName + " = '" + segmentName + "'" +
285357
jdbcEnvironment.getDialect().getForUpdateString();
286358

287359
update = "update " +
@@ -291,59 +363,24 @@ public void configure(Type type, Properties params, ServiceRegistry serviceRegis
291363
" = ? where " +
292364
valueColumnName +
293365
" = ? and " +
294-
pkColumnName +
366+
segmentColumnName +
295367
" = '" +
296-
keyValue
368+
segmentName
297369
+ "'";
298370

299371
insert = "insert into " + tableName +
300-
"(" + pkColumnName + ", " + valueColumnName + ") " +
301-
"values('" + keyValue + "', ?)";
302-
303-
304-
//hilo config
305-
maxLo = ConfigurationHelper.getInt( MAX_LO, params, Short.MAX_VALUE );
306-
returnClass = type.getReturnedClass();
372+
"(" + segmentColumnName + ", " + valueColumnName + ") " +
373+
"values('" + segmentName + "', ?)";
307374

308-
if ( maxLo >= 1 ) {
309-
hiloOptimizer = new LegacyHiLoAlgorithmOptimizer( returnClass, maxLo );
310-
}
311-
}
312375

313-
@Override
314-
public void registerExportables(Database database) {
315-
final Namespace namespace = database.locateNamespace(
316-
qualifiedTableName.getCatalogName(),
317-
qualifiedTableName.getSchemaName()
318-
);
319376

320-
final Table table = namespace.createTable( qualifiedTableName.getObjectName(), false );
321-
table.setPrimaryKey( new PrimaryKey() );
322-
323-
final Column pkColumn = new ExportableColumn(
324-
database,
325-
table,
326-
pkColumnName,
327-
StringType.INSTANCE,
328-
database.getDialect().getTypeName( Types.VARCHAR, keySize, 0, 0 )
329-
);
330-
table.addColumn( pkColumn );
331-
table.getPrimaryKey().addColumn( pkColumn );
332-
333-
final Column valueColumn = new ExportableColumn(
334-
database,
335-
table,
336-
valueColumnName,
337-
LongType.INSTANCE
338-
);
339-
table.addColumn( valueColumn );
340377
}
341378

342379
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
343380
return new String[] {
344381
dialect.getCreateTableString()
345382
+ ' ' + tableName + " ( "
346-
+ pkColumnName + ' ' + dialect.getTypeName( Types.VARCHAR, keySize, 0, 0 ) + ", "
383+
+ segmentColumnName + ' ' + dialect.getTypeName( Types.VARCHAR, keySize, 0, 0 ) + ", "
347384
+ valueColumnName + ' ' + dialect.getTypeName( Types.INTEGER )
348385
+ " )" + dialect.getTableTypeString()
349386
};

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@ public void configure(Type type, Properties params, ServiceRegistry serviceRegis
349349
identifierType = type;
350350

351351
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
352-
final Dialect dialect = jdbcEnvironment.getDialect();
353352

354353
qualifiedTableName = determineGeneratorTableName( params, jdbcEnvironment );
355354
segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment );

hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/Db2GenerationTest.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
1515
import org.hibernate.cfg.AvailableSettings;
1616
import org.hibernate.dialect.DB2Dialect;
17+
import org.hibernate.id.MultipleHiLoPerTableGenerator;
1718
import org.hibernate.id.enhanced.TableGenerator;
1819
import org.hibernate.mapping.Table;
1920
import org.hibernate.type.IntegerType;
@@ -24,15 +25,14 @@
2425
import org.junit.Test;
2526

2627
import static org.junit.Assert.assertEquals;
27-
import static org.junit.Assert.assertTrue;
2828

2929
/**
3030
* @author Steve Ebersole
3131
*/
3232
public class Db2GenerationTest extends BaseUnitTestCase {
3333
@Test
3434
@TestForIssue( jiraKey = "HHH-9850" )
35-
public void testDb2TableGeneratorCreation() {
35+
public void testNewGeneratorTableCreationOnDb2() {
3636
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
3737
.applySetting( AvailableSettings.DIALECT, DB2Dialect.class.getName() )
3838
.build();
@@ -66,4 +66,34 @@ private void assertContains(String subStr, String str) {
6666
Assert.fail( "String [" + str + "] did not contain expected substring [" + subStr + "]" );
6767
}
6868
}
69+
@Test
70+
@TestForIssue( jiraKey = "HHH-9850" )
71+
public void testLegacyGeneratorTableCreationOnDb2() {
72+
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
73+
.applySetting( AvailableSettings.DIALECT, DB2Dialect.class.getName() )
74+
.build();
75+
76+
try {
77+
Metadata metadata = new MetadataSources( ssr )
78+
.buildMetadata();
79+
80+
assertEquals( 0, metadata.getDatabase().getDefaultNamespace().getTables().size() );
81+
82+
MultipleHiLoPerTableGenerator generator = new MultipleHiLoPerTableGenerator();
83+
84+
Properties properties = new Properties();
85+
generator.configure( IntegerType.INSTANCE, properties, ssr );
86+
87+
generator.registerExportables( metadata.getDatabase() );
88+
89+
assertEquals( 1, metadata.getDatabase().getDefaultNamespace().getTables().size() );
90+
91+
final Table table = metadata.getDatabase().getDefaultNamespace().getTables().iterator().next();
92+
final String[] createCommands = new DB2Dialect().getTableExporter().getSqlCreateStrings( table, metadata );
93+
assertContains( "sequence_name varchar(255) not null", createCommands[0] );
94+
}
95+
finally {
96+
StandardServiceRegistryBuilder.destroy( ssr );
97+
}
98+
}
6999
}

0 commit comments

Comments
 (0)