javahibernatespring-bootjpaspring-boot-jpa

Spring JPA composite PK with one auto-incremented


I'm trying to persist an entity with a composite primary key, one of which should be auto-incremented, using Spring Boot 2.1.2.RELEASE + JPA (using starters) and MySQL (InnoDB). I have an abstract class with 2 non-abstract subclasses and I'm using single table strategy for inheritance.

I've been looking around how to go about accomplishing this but every post that seems to be looking for the same as me ends up without an answer (example).

My current setup looks like this (setters, getters and constructors omitted for clarity).

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@IdClass(MyEntityPK.class)
public abstract class MyEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    long pk1; //Should be auto-incremented

    @Id
    OffsetDateTime pk2;

    //More fields...
public class MyEntityPK implements Serializable {
   private static final long serialVersionUID = 1L;

   long pk1;
   OffsetDateTime pk2;
}
@Repository
public interface MyEntityRepository extends CrudRepository<MyEntity, MyEntityPK> {
   //Some methods like save().
}


The first problem I found was with the table generation. MySQL allows creating the PK if it's declared like

PRIMARY KEY (pk1, pk2)

but not in the different order

PRIMARY KEY (pk2, pk1)

When creating the table, Hibernate uses the second one, and thus failing. Also tried to change fields order but to no avail. I think this might be an issue with Hibernate, but I'm not sure. Can anyone confirm? Anyway, I solved that by creating the DDL script manually and disabling automatic DDL by Hibernate.

The next issue, for which I did not find an answer, was that the entities were saved correctly in the table, but the id returned by the save method of the repository was always 0. I was manually setting pk2 before saving, while leaving pk1 to be completed by the DB.

entity.setPk2(OffsetDateTime.now());
Entity savedEntity = repository.save(entity); //savedEntity has id 1 in table but 0 in code

While debugging, I arrived at this isNew() method. It turns out it was returning false because MyEntityPK was different from null (I had set pk2). This of course is not what I expected, since I actually am saving a new entity, given that my PK is composite and I left one field blank. So, I also think this may be a bug. There are some classes that override isNew() method, but it seems they are not being used.

Am I doing something wrong or is this a bug? Help appreciated :)


Solution

  • @GeneratedValue(strategy = GenerationType.IDENTITY) on @EmbededId or @IdClass is not supported.

    Here is a bug related exactly to your request which was rejected by hibernate https://hibernate.atlassian.net/browse/ANN-268

    Here is another much more recent bug reporteing the same. Notice here Vlad Mihalcea comment:

    This is not a Blocker, it's merely a Major issue. For instance, the JPA spec does not even say if generated identifiers are allowed in a Composite Identifier.

    Anyway, this only works as @IdClass with sequence-based identifiers, as explained in this article.

    https://hibernate.atlassian.net/browse/HHH-9662

    The article in the quotation explains how to map @GeneratedValue(strategy = GenerationType.SEQUENCE) on composite.