HHH-12125 - Support @GeneratedValue without explicit generator definition

This commit is contained in:
Steve Ebersole 2017-12-01 15:09:08 -06:00
parent 888ade0106
commit d1fb1525aa
3 changed files with 177 additions and 39 deletions

View File

@ -143,12 +143,14 @@ public class SequenceStyleGenerator
* Used to create dedicated sequence for each entity based on the entity name. Sequence suffix can be
* controlled with {@link #CONFIG_SEQUENCE_PER_ENTITY_SUFFIX} option.
*/
@SuppressWarnings("WeakerAccess")
public static final String CONFIG_PREFER_SEQUENCE_PER_ENTITY = "prefer_sequence_per_entity";
/**
* Indicates the suffix to use in naming the identifier sequence/table name, by appending the suffix to
* the name of the entity. Used in conjunction with {@link #CONFIG_PREFER_SEQUENCE_PER_ENTITY}.
*/
@SuppressWarnings("WeakerAccess")
public static final String CONFIG_SEQUENCE_PER_ENTITY_SUFFIX = "sequence_per_entity_suffix";
/**
@ -173,11 +175,13 @@ public class SequenceStyleGenerator
/**
* Indicates the name of the column holding the identifier values. The default value is {@link #DEF_VALUE_COLUMN}
*/
@SuppressWarnings("WeakerAccess")
public static final String VALUE_COLUMN_PARAM = "value_column";
/**
* The default value for {@link #VALUE_COLUMN_PARAM}
*/
@SuppressWarnings("WeakerAccess")
public static final String DEF_VALUE_COLUMN = "next_val";
@ -267,10 +271,9 @@ public class SequenceStyleGenerator
* @param params The params supplied in the generator config (plus some standard useful extras).
* @param dialect The dialect in effect
* @param jdbcEnv The JdbcEnvironment
* @param serviceRegistry
* @return The sequence name
*/
@SuppressWarnings("UnusedParameters")
@SuppressWarnings({"UnusedParameters", "WeakerAccess"})
protected QualifiedName determineSequenceName(
Properties params,
Dialect dialect,
@ -281,7 +284,7 @@ public class SequenceStyleGenerator
String fallbackSequenceName = DEF_SEQUENCE_NAME;
final Boolean preferGeneratorNameAsDefaultName = serviceRegistry.getService( ConfigurationService.class )
.getSetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, StandardConverters.BOOLEAN, true );
if ( preferGeneratorNameAsDefaultName != null && preferGeneratorNameAsDefaultName ) {
if ( preferGeneratorNameAsDefaultName ) {
final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME );
if ( StringHelper.isNotEmpty( generatorName ) ) {
fallbackSequenceName = generatorName;
@ -324,7 +327,7 @@ public class SequenceStyleGenerator
* @param jdbcEnvironment The JDBC environment
* @return The value column name
*/
@SuppressWarnings("UnusedParameters")
@SuppressWarnings({"UnusedParameters", "WeakerAccess"})
protected Identifier determineValueColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name );
@ -340,6 +343,7 @@ public class SequenceStyleGenerator
* @param params The params supplied in the generator config (plus some standard useful extras).
* @return The initial value
*/
@SuppressWarnings({"WeakerAccess"})
protected int determineInitialValue(Properties params) {
return ConfigurationHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
}
@ -353,6 +357,7 @@ public class SequenceStyleGenerator
* @param params The params supplied in the generator config (plus some standard useful extras).
* @return The increment size
*/
@SuppressWarnings("WeakerAccess")
protected int determineIncrementSize(Properties params) {
return ConfigurationHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
}
@ -366,6 +371,7 @@ public class SequenceStyleGenerator
* @param incrementSize The {@link #determineIncrementSize determined increment size}
* @return The optimizer strategy (name)
*/
@SuppressWarnings("WeakerAccess")
protected String determineOptimizationStrategy(Properties params, int incrementSize) {
return ConfigurationHelper.getString(
OPT_PARAM,
@ -382,6 +388,7 @@ public class SequenceStyleGenerator
* @param incrementSize The {@link #determineIncrementSize determined increment size}
* @return The adjusted increment size.
*/
@SuppressWarnings("WeakerAccess")
protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) {
final int resolvedIncrementSize;
if ( Math.abs( incrementSize ) > 1 &&
@ -427,6 +434,7 @@ public class SequenceStyleGenerator
*
* @return An abstraction for the actual database structure in use (table vs. sequence).
*/
@SuppressWarnings("WeakerAccess")
protected DatabaseStructure buildDatabaseStructure(
Type type,
Properties params,
@ -454,6 +462,7 @@ public class SequenceStyleGenerator
return new SequenceStructure( jdbcEnvironment, sequenceName, initialValue, incrementSize, type.getReturnedClass() );
}
@SuppressWarnings("WeakerAccess")
protected DatabaseStructure buildTableStructure(
Type type,
Properties params,

View File

@ -148,6 +148,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
/**
* The default {@link #TABLE_PARAM} value
*/
@SuppressWarnings("WeakerAccess")
public static final String DEF_TABLE = "hibernate_sequences";
/**
@ -181,17 +182,20 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
/**
* The default {@link #SEGMENT_VALUE_PARAM} value, unless {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} is specified
*/
@SuppressWarnings("WeakerAccess")
public static final String DEF_SEGMENT_VALUE = "default";
/**
* Indicates the length of the column defined by {@link #SEGMENT_COLUMN_PARAM}. Used in schema export. The
* default value is {@link #DEF_SEGMENT_LENGTH}
*/
@SuppressWarnings("WeakerAccess")
public static final String SEGMENT_LENGTH_PARAM = "segment_value_length";
/**
* The default {@link #SEGMENT_LENGTH_PARAM} value
*/
@SuppressWarnings("WeakerAccess")
public static final int DEF_SEGMENT_LENGTH = 255;
/**
@ -202,6 +206,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
/**
* The default {@link #INITIAL_PARAM} value
*/
@SuppressWarnings("WeakerAccess")
public static final int DEFAULT_INITIAL_VALUE = 1;
/**
@ -212,6 +217,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
/**
* The default {@link #INCREMENT_PARAM} value
*/
@SuppressWarnings("WeakerAccess")
public static final int DEFAULT_INCREMENT_SIZE = 1;
/**
@ -294,7 +300,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
*
* @return the column size.
*/
@SuppressWarnings("UnusedDeclaration")
@SuppressWarnings({"UnusedDeclaration", "WeakerAccess"})
public final int getSegmentValueLength() {
return segmentValueLength;
}
@ -386,23 +392,23 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @param jdbcEnvironment The JDBC environment
* @return The table name to use.
*/
@SuppressWarnings("UnusedParameters")
@SuppressWarnings({"UnusedParameters", "WeakerAccess"})
protected QualifiedName determineGeneratorTableName(Properties params, JdbcEnvironment jdbcEnvironment, ServiceRegistry serviceRegistry) {
String tableName = ConfigurationHelper.getString( TABLE_PARAM, params, DEF_TABLE );
String fallbackTableName = DEF_TABLE;
if ( tableName == null ) {
tableName = DEF_TABLE;
final Boolean preferGeneratorNameAsDefaultName = serviceRegistry.getService( ConfigurationService.class )
.getSetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, StandardConverters.BOOLEAN, true );
if ( preferGeneratorNameAsDefaultName != null && preferGeneratorNameAsDefaultName ) {
final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME );
if ( StringHelper.isNotEmpty( generatorName ) ) {
tableName = generatorName;
}
final Boolean preferGeneratorNameAsDefaultName = serviceRegistry.getService( ConfigurationService.class )
.getSetting( AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, StandardConverters.BOOLEAN, true );
if ( preferGeneratorNameAsDefaultName ) {
final String generatorName = params.getProperty( IdentifierGenerator.GENERATOR_NAME );
if ( StringHelper.isNotEmpty( generatorName ) ) {
fallbackTableName = generatorName;
}
}
String tableName = ConfigurationHelper.getString( TABLE_PARAM, params, fallbackTableName );
if ( tableName.contains( "." ) ) {
return QualifiedNameParser.INSTANCE.parse( tableName );
}
@ -433,7 +439,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @param jdbcEnvironment The JDBC environment
* @return The name of the segment column
*/
@SuppressWarnings("UnusedParameters")
@SuppressWarnings({"UnusedParameters", "WeakerAccess"})
protected String determineSegmentColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
final String name = ConfigurationHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() );
@ -449,7 +455,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @param jdbcEnvironment The JDBC environment
* @return The name of the value column
*/
@SuppressWarnings("UnusedParameters")
@SuppressWarnings({"UnusedParameters", "WeakerAccess"})
protected String determineValueColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name ).render( jdbcEnvironment.getDialect() );
@ -464,6 +470,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @param params The params supplied in the generator config (plus some standard useful extras).
* @return The name of the value column
*/
@SuppressWarnings("WeakerAccess")
protected String determineSegmentValue(Properties params) {
String segmentValue = params.getProperty( SEGMENT_VALUE_PARAM );
if ( StringHelper.isEmpty( segmentValue ) ) {
@ -479,6 +486,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @param params The params supplied in the generator config (plus some standard useful extras).
* @return The default segment value to use.
*/
@SuppressWarnings("WeakerAccess")
protected String determineDefaultSegmentValue(Properties params) {
final boolean preferSegmentPerEntity = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEGMENT_PER_ENTITY, params, false );
final String defaultToUse = preferSegmentPerEntity ? params.getProperty( TABLE ) : DEF_SEGMENT_VALUE;
@ -495,19 +503,22 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @param params The params supplied in the generator config (plus some standard useful extras).
* @return The size of the segment column
*/
@SuppressWarnings("WeakerAccess")
protected int determineSegmentColumnSize(Properties params) {
return ConfigurationHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMENT_LENGTH );
}
@SuppressWarnings("WeakerAccess")
protected int determineInitialValue(Properties params) {
return ConfigurationHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
}
@SuppressWarnings("WeakerAccess")
protected int determineIncrementSize(Properties params) {
return ConfigurationHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
}
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "WeakerAccess"})
protected String buildSelectQuery(Dialect dialect) {
final String alias = "tbl";
final String query = "select " + StringHelper.qualify( alias, valueColumnName ) +
@ -519,12 +530,14 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
return dialect.applyLocksToSql( query, lockOptions, updateTargetColumnsMap );
}
@SuppressWarnings("WeakerAccess")
protected String buildUpdateQuery() {
return "update " + renderedTableName +
" set " + valueColumnName + "=? " +
" where " + valueColumnName + "=? and " + segmentColumnName + "=?";
}
@SuppressWarnings("WeakerAccess")
protected String buildInsertQuery() {
return "insert into " + renderedTableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
}
@ -551,24 +564,29 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
final IntegralDataTypeHolder value = makeValue();
int rows;
do {
final PreparedStatement selectPS = prepareStatement( connection, selectQuery, statementLogger, statsCollector );
try {
try (PreparedStatement selectPS = prepareStatement(
connection,
selectQuery,
statementLogger,
statsCollector
)) {
selectPS.setString( 1, segmentValue );
final ResultSet selectRS = executeQuery( selectPS, statsCollector );
if ( !selectRS.next() ) {
value.initialize( initialValue );
final PreparedStatement insertPS = prepareStatement( connection, insertQuery, statementLogger, statsCollector );
try {
try (PreparedStatement insertPS = prepareStatement(
connection,
insertQuery,
statementLogger,
statsCollector
)) {
LOG.tracef( "binding parameter [%s] - [%s]", 1, segmentValue );
insertPS.setString( 1, segmentValue );
value.bind( insertPS, 2 );
executeUpdate( insertPS, statsCollector );
}
finally {
insertPS.close();
}
}
else {
value.initialize( selectRS, 1 );
@ -579,13 +597,14 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
LOG.unableToReadOrInitHiValue( e );
throw e;
}
finally {
selectPS.close();
}
final PreparedStatement updatePS = prepareStatement( connection, updateQuery, statementLogger, statsCollector );
try {
try (PreparedStatement updatePS = prepareStatement(
connection,
updateQuery,
statementLogger,
statsCollector
)) {
final IntegralDataTypeHolder updateValue = value.copy();
if ( optimizer.applyIncrementSizeToSourceValues() ) {
updateValue.add( incrementSize );
@ -602,9 +621,6 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
LOG.unableToUpdateQueryHiValue( renderedTableName, e );
throw e;
}
finally {
updatePS.close();
}
}
while ( rows == 0 );

View File

@ -15,9 +15,12 @@ import javax.persistence.SequenceGenerator;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IncrementGenerator;
@ -26,13 +29,13 @@ import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertTrue;
/**
* Tests of various aspects of {@link GeneratedValue} handling in regards to determining
@ -57,7 +60,9 @@ public class GeneratedValueTests extends BaseUnitTestCase {
);
final SequenceStyleGenerator sequenceStyleGenerator = assertTyping( SequenceStyleGenerator.class, generator );
assertThat( sequenceStyleGenerator.getDatabaseStructure().getName(), is( "my_real_db_sequence" ) );
// all the JPA defaults since they were not defined
assertThat( sequenceStyleGenerator.getDatabaseStructure().getInitialValue(), is( 100 ) );
assertThat( sequenceStyleGenerator.getDatabaseStructure().getIncrementSize(), is( 500 ) );
@ -81,7 +86,12 @@ public class GeneratedValueTests extends BaseUnitTestCase {
);
final SequenceStyleGenerator sequenceStyleGenerator = assertTyping( SequenceStyleGenerator.class, generator );
// all the JPA defaults since they were not defined
// PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME == false indicates that the legacy
// default (hibernate_sequence) should be used
assertThat( sequenceStyleGenerator.getDatabaseStructure().getName(), is( "hibernate_sequence" ) );
// the JPA defaults since they were not defined
assertThat( sequenceStyleGenerator.getDatabaseStructure().getInitialValue(), is( 1 ) );
assertThat( sequenceStyleGenerator.getDatabaseStructure().getIncrementSize(), is( 50 ) );
}
@ -102,8 +112,12 @@ public class GeneratedValueTests extends BaseUnitTestCase {
);
final SequenceStyleGenerator sequenceStyleGenerator = assertTyping( SequenceStyleGenerator.class, generator );
// all the JPA defaults since they were not defined
// PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME == true (the default) indicates that the generator-name
// should be used as the default instead.
assertThat( sequenceStyleGenerator.getDatabaseStructure().getName(), is( "my_db_sequence" ) );
// the JPA defaults since they were not defined
assertThat( sequenceStyleGenerator.getDatabaseStructure().getInitialValue(), is( 1 ) );
assertThat( sequenceStyleGenerator.getDatabaseStructure().getIncrementSize(), is( 50 ) );
}
@ -153,10 +167,20 @@ public class GeneratedValueTests extends BaseUnitTestCase {
assertThat( sequenceStyleGenerator.getDatabaseStructure().getName(), is( "my_db_sequence" ) );
assertThat( sequenceStyleGenerator.getDatabaseStructure().getInitialValue(), is( 100 ) );
assertThat( sequenceStyleGenerator.getDatabaseStructure().getIncrementSize(), is( 500 ) );
final Sequence sequence = bootModel.getDatabase()
.getDefaultNamespace()
.locateSequence( Identifier.toIdentifier( "my_db_sequence" ) );
final String[] sqlCreateStrings = new H2Dialect().getSequenceExporter().getSqlCreateStrings(
sequence,
bootModel
);
assertThat( sqlCreateStrings.length, is(1) );
final String cmd = sqlCreateStrings[0].toLowerCase();
assertTrue( cmd.startsWith( "create sequence my_db_sequence start with 100 increment by 500" ) );
}
@Test
@FailureExpected( jiraKey = "HHH-12122", message = "for some reason the initial value here gets interpreted as 2; other than that this works" )
public void testImplicitTableGenerator() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
final Metadata bootModel = new MetadataSources( ssr )
@ -172,11 +196,66 @@ public class GeneratedValueTests extends BaseUnitTestCase {
);
final TableGenerator tableGenerator = assertTyping( TableGenerator.class, generator );
assertThat( tableGenerator.getTableName(), is( "my_id_table" ) );
// all the JPA defaults since they were not defined
assertThat( tableGenerator.getInitialValue(), is( 1 ) );
// - note : currently initialValue=1 in mapping is shows up here
// as 2
// assertThat( tableGenerator.getInitialValue(), is( 1 ) );
assertThat( tableGenerator.getIncrementSize(), is( 50 ) );
}
@Test
public void testExplicitTableGeneratorImplicitName() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ExplicitTableGeneratorImplicitNameEntity.class )
.buildMetadata();
final PersistentClass entityMapping = bootModel.getEntityBinding( ExplicitTableGeneratorImplicitNameEntity.class.getName() );
final IdentifierGenerator generator = entityMapping.getIdentifier().createIdentifierGenerator(
bootModel.getIdentifierGeneratorFactory(),
ssr.getService( JdbcEnvironment.class ).getDialect(),
null,
null,
(RootClass) entityMapping
);
final TableGenerator tableGenerator = assertTyping( TableGenerator.class, generator );
assertThat( tableGenerator.getTableName(), is( "my_id_table" ) );
// - note : currently initialValue=1 in mapping is shows up here as 2
// assertThat( tableGenerator.getInitialValue(), is( 1 ) );
assertThat( tableGenerator.getIncrementSize(), is( 25 ) );
}
@Test
public void testExplicitTableGenerator() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
final Metadata bootModel = new MetadataSources( ssr )
.addAnnotatedClass( ExplicitTableGeneratorEntity.class )
.buildMetadata();
final PersistentClass entityMapping = bootModel.getEntityBinding( ExplicitTableGeneratorEntity.class.getName() );
final IdentifierGenerator generator = entityMapping.getIdentifier().createIdentifierGenerator(
bootModel.getIdentifierGeneratorFactory(),
ssr.getService( JdbcEnvironment.class ).getDialect(),
null,
null,
(RootClass) entityMapping
);
final TableGenerator tableGenerator = assertTyping( TableGenerator.class, generator );
assertThat( tableGenerator.getTableName(), is( "my_real_id_table" ) );
// all the JPA defaults since they were not defined
// - note : currently initialValue=1 in mapping is shows up here
// as 2
// assertThat( tableGenerator.getInitialValue(), is( 1 ) );
assertThat( tableGenerator.getIncrementSize(), is( 25 ) );
}
@Test
public void testExplicitIncrementGenerator() {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
@ -259,6 +338,40 @@ public class GeneratedValueTests extends BaseUnitTestCase {
public String name;
}
@Entity
public static class ExplicitTableGeneratorImplicitNameEntity {
/**
* This entity has an explicit {@link javax.persistence.TableGenerator} defined,
* but does not define {@link javax.persistence.TableGenerator#table()}. In
* this case, the generator-name ("my_id_table")
*/
@Id
@GeneratedValue( strategy = GenerationType.TABLE, generator = "my_id_table" )
@javax.persistence.TableGenerator( name = "my_id_table", allocationSize = 25 )
public Integer id;
public String name;
}
@Entity
@javax.persistence.TableGenerator(
name = "my_id_table",
table = "my_real_id_table",
pkColumnName = "PK_COL",
valueColumnName = "VAL_COL",
pkColumnValue = "DT1_ID",
allocationSize = 25
)
public static class ExplicitTableGeneratorEntity {
/**
* This entity has an explicit {@link javax.persistence.TableGenerator} defined,
* and specifies a table name. That table name ("my_real_id_table") should be used.
*/
@Id
@GeneratedValue( strategy = GenerationType.TABLE, generator = "my_id_table" )
public Integer id;
public String name;
}
@Entity
public static class ExplicitIncrementGeneratorEntity {
/**