4

My classes use an ID like

@Id @Generated(GenerationTime.INSERT) @GeneratedValue private Integer id; 

This works perfectly for H2 (supporting sequences) and gets interpreted for MySql by creating a helper table hibernate_sequence. Using this answer, everything looks the way I want, especially using a single sequence for all tables.

One thing seems to be wrong: There are multiple rows in the helper table. My id is declared in a @MappedSuperclass and during initialization, for each concrete class this line gets executed:

insert into hibernate_sequence values ( 1 ) 

This is obviously wrong: there is a line per table there and each contains the same value (initially one; when changed, they all change in the same way, as the SQL is update hibernate_sequence set next_val=? where next_val=?, so it effects all the rows in the same way).

It's harmless, but I wonder: Is it a bug or am I doing something wrong?

1
  • I deleted my answer; I did not understand your issue properly. I think that you should edit the question to include the explanation you provided in the comment of my answer. Commented Jan 7, 2016 at 20:33

3 Answers 3

4
+50

If you want to make it works you need to use other strategy for now:

@GenericGenerator( name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator" ) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") 

What about org.hibernate.id.enhanced.SequenceStyleGenerator:

I believe there is an issue in how hibernate initializes shared single row sequence table.

For hibernare 5.0.6.Final Source of the issue lies in org.hibernate.boot.internal.InFlightMetadataCollectorImpl class in

private void processExportableProducers(MetadataBuildingContext buildingContext) { // for now we only handle id generators as ExportableProducers final Dialect dialect = getDatabase().getJdbcEnvironment().getDialect(); final String defaultCatalog = extractName( getDatabase().getDefaultNamespace().getName().getCatalog(), dialect ); final String defaultSchema = extractName( getDatabase().getDefaultNamespace().getName().getSchema(), dialect ); for ( PersistentClass entityBinding : entityBindingMap.values() ) { if ( entityBinding.isInherited() ) { continue; } // *************************************************************************** // For Instance, it does not filter out the same entityBinding.getIdentifier() // and make initialization multiple time // *************************************************************************** handleIdentifierValueBinding( entityBinding.getIdentifier(), dialect, defaultCatalog, defaultSchema, (RootClass) entityBinding ); } for ( Collection collection : collectionBindingMap.values() ) { if ( !IdentifierCollection.class.isInstance( collection ) ) { continue; } handleIdentifierValueBinding( ( (IdentifierCollection) collection ).getIdentifier(), dialect, defaultCatalog, defaultSchema, null ); } } 
Sign up to request clarification or add additional context in comments.

1 Comment

You start with "to make it work", which sounds wrong (as it does work now, it's just not nice) and confused me while quickly scanning your answer. But you seem to be right. I filed HHH-10441 and linked it to your answer.
4

Same Sequence Generator

Presume there is a Base class annotated with @MappedSuperclass. There are also A and B classes extending Base class; If you annotate Id field in Base class with @SequenceGenerator, all subclasses of Base share the same sequence generator and will increament/use the same sequence in the database for their ids. This is ofcourse harmless but results in ugly numbers for ids :

@MappedSuperclass public class Person { @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="id_gen") @SequenceGenerator(name="id_gen", sequenceName="a_seq", allocationSize=1) private Long id; } @Entity public class A extends Base { } @Entity public class B extends Base { } 

This is what their ids look like after adding A, then B, then A :

A{id=1, name='...'} B{id=2, name='...'} A{id=3, name='...'} 

Distinct Sequence Generator

A nicer way would be having a fresh sequence for each table which can be implemented through assigning different sequence generators for different subclasses :

@MappedSuperclass public class Person { @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="id_gen") private Long id; } @Entity @SequenceGenerator(name="id_gen", sequenceName="a_seq", allocationSize=1) public class A extends Base { } @Entity @SequenceGenerator(name="id_gen", sequenceName="b_seq", allocationSize=1) public class B extends Base { } 

Their ids will look like this after adding A, then B, then A :

A{id=1, name='...'} B{id=1, name='...'} A{id=2, name='...'} 

1 Comment

This question about why helper table is initialized multiple times for shared sequence with org.hibernate.id.enhanced.SequenceStyleGenerator strategy.
0

A class annotated with @MappedSuperclass has no table of it's own. Only the entities that inherit that class have tables. So according to that the initialization has no bug, because, the sequence should be different for each concrete entity. So this is not a bug, rather it's the expected behavior.

Regarding update, if all three rows get updated when you insert a record in a SINGLE table then there is definitely a bug. But I am not sure if this is the scenario that you are experiencing.

2 Comments

There's nothing but the single column next_val in hibernate_sequence, so there's no way to assign different rows to different concrete classes. Indeed, all three rows get updated when I insert a record in a single table.
I disagree about expected behaviour because it is one sequence for all tables that you want. If you will use some native sequence it will be work as one for many. So obviously here we have wrong initialization of sequence implemented as the table.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.