Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I combine TABLE_PER_CLASS and GenerationType.IDENTITY

Dear future reader: After working through Sannes answer and the link he provides, I decided to go with a Sequence instead. Newer Hibernates take care that it works on MySQL as well.


I have an abstract class that contains some basic values that I want to have in all my entities (such as id, creation date, creation user, etc.). Currently I struggle with the combination of having a table per concrete class and having its id work.

It seems I can either have a table per class OR a single place where I configure the ID, but not both. That seems very odd and unlikely to me, so I want to make sure that this is the case.

Versions that might be relevant: hibernate 5, mysql 5.5, java 1.8, spring 4.3.

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@MappedSuperclass
public abstract class GeneralData implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false)
    protected int id;
    protected String name;

    // getters, setters, constructors, etc.
}

Example subclass:

@Entity
public class ExampleClass extends GeneralData {
    // own values, constructors, getters, setters, etc.
}

Error I get:

Cannot use identity column key generation with <union-subclass> mapping for: org.example.ExampleClass

Things I've tried:

  • I have tried to make GeneralData an @Entity, but that didn't help.
  • I tried to use a GenerationType.TABLE but that failed because the key is too big. (Also, I'd rather not have this, IDENTITY is what I want) Error: Specified key was too long; max key length is 1000 bytes

I'd rather not move the id column to each and every concrete class. I'm totally fine if the ID is unique only for a single table and not via the whole dependency tree.

If I should upgrade my libs, database, etc. please keep in mind that such can only be done with a lot of extra work (for internal reasons). This would be a last resort and I'd like to see some evidence that it helps first.

like image 471
Angelo Fuchs Avatar asked Nov 23 '17 12:11

Angelo Fuchs


1 Answers

A requirement for all child objects of the same parent entity is that they have unique IDs among them.

Say for example you have a parent Entity GeneralData and then store subtype A with id=1. This would be perfectly legal:

GeneralData loaded = entitymanager.find( GeneralData.class, 1 );

And of course the type of loaded will be A: the subclass. The mapping must ensure there is no ambiguity: NO other subclass must be allowed to also use "1" as an ID, even if it's mapped in a different table.

In your case you're asking Hibernate to store each different subtype of GeneralData into its own separate Table.

You're also asking that MySQL - which is not aware of these tables being related in any way - to assign unique identifiers automatically.

It's not possible to instruct MySQL that each of these tables need to use the same Identity "sequence" as these are not real Sequences: so this mapping is illegal! It would get you in trouble as it will assign overlapping IDs to different subtypes of your parent entity, violating the semantics described above.

This is a good example of the limitations of using IDENTITY; besides, IDENTITY is not very efficient as it implies that Hibernate needs to write the entity immediately as soon as it's persisted rather than being able to more efficiently postpone the flush operation to a more fitting time, and so doing possibly batch writes or skip them altogether (especially if transactions are aborted, but also if you update the entity after the persist but still in the same transaction).

I'd suggest to have a look at different Identifier generator strategies for such hierarchies, and in particular Optimizers might vastly improve the performance of your application.

like image 125
Sanne Avatar answered Oct 03 '22 22:10

Sanne