JPA / Hibernate, duplicate pkey error when updating entity that is a subclass of a base class that uses IdClass for composite primary key

Description

How to reproduce this issue

https://github.com/cuipengfei/Spikes/tree/master/jpa/ClassIdUpdateIssue

this code can reproduce the issue, just run the main method then the error will happen.

 

https://github.com/gregturn/spring-data-jpa-id-class-issues/tree/main/src/test/java/com/example/demo

Just make sure you have docker installed and run these 👆 unit tests.

The unit test with EmbeddedId will be ok.

But the one with IdClass will fail, while we expect it to be successful.

Issue Description

There is a base class like this:

@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorFormula("case when vip_number is not null then 'vip' else 'normal' end") @DiscriminatorValue("normal") @IdClass(CustomerPK.class) public class CustomerWithIdClass implements Serializable { private String firstName; private String lastName; @Id private Long versionId; @Id private Long unitId; protected CustomerWithIdClass() { } // getter and setters ...... }

Its IdClass is like this:

@NoArgsConstructor @EqualsAndHashCode @Embeddable public class CustomerPK implements Serializable { private Long unitId; private Long versionId; public void setUnitId(Long unitId) { this.unitId = unitId; } public void setVersionId(Long versionId) { this.versionId = versionId; } }

Then it has a subclass:

@Entity @DiscriminatorValue("vip") public class VipCustomerWithIdClass extends CustomerWithIdClass { private String vipNumber; public VipCustomerWithIdClass() { } public VipCustomerWithIdClass(String firstName, String lastName, String vipNumber) { super(firstName, lastName); this.vipNumber = vipNumber; } public String getVipNumber() { return vipNumber; } public void setVipNumber(String vipNumber) { this.vipNumber = vipNumber; } }

The subclass only adds one additional field, nothing else fancy.

Then when I try to persist an instance of the subclass like this:

CustomerWithIdClass customer = new CustomerWithIdClass("a", "b"); customer.setVersionId(123L); customer.setUnitId(456L); repository.save(customer);//save object of base class, ok customer.setFirstName("a2"); repository.save(customer);//modify object of base class and save again, ok VipCustomerWithIdClass vipCustomer = new VipCustomerWithIdClass("a", "b", "888"); vipCustomer.setVersionId(987L); vipCustomer.setUnitId(654L); repository.save(vipCustomer);//save object of subclass, ok vipCustomer.setVipNumber("999"); repository.save(vipCustomer);//modify object of subclass and save again, NOT OK // ↑ THIS FAILS BECAUSE OF PRIMARY KEY CONFLICT. INSERT STATEMENT WAS USED INSTEAD OF UPDATE, WHY? // this failure only happens when: // 1. base class uses IdClass for composite primary key // 2. saving an instance of the subclass for the second time after modification

The Error

Then there will be an error of duplicate pkey when I try to save the instance of the subclass for the second time after some modification.

The error seems to be related with the @IdClass annotation, because I have tried using @EmbeddedId and @Embeddable for composite pkey, then the error does not happen.

The question

What is the root reason of this issue? Is @IdClass not supposed to be used for this scenario?

links

https://stackoverflow.com/questions/75147518/jpa-hibernate-duplicate-pkey-error-when-updating-entity-that-is-a-subclass-of

https://github.com/spring-projects/spring-data-jpa/issues/2767

Activity

Show:

Gavin King November 29, 2024 at 3:29 PM

The issue has already been fixed. I have added your test to make sure it stays fixed.

Cui Pengfei July 27, 2023 at 10:57 AM

Hi Community, any chance this PR will get merged?

huang di February 11, 2023 at 12:35 PM
Edited

The root cause is that joinedsubclasses use their own attributes to judge whether there is an idenfierMapper. The correct behavior is to use the attributes of the parent class(superClass) to judge in the inheritance scenario. canExtractIdOutOfEntity() return false due to this problem, then the ID is null and the problem occurs. You need to delegate the hasIdentifierMapper method to superClass to fix this.

 

I can fix this, but don't understand the process of submitting code

CPF January 31, 2023 at 2:02 AM

Hello Hibernate community, any updates on this issue?

jschauder January 24, 2023 at 10:22 AM

Further investigation showed that the problem occurs only when the two merge calls happen in separate transactions. See https://github.com/gregturn/spring-data-jpa-id-class-issues/pull/1

Fixed

Details

Assignee

Reporter

Components

Fix versions

Affects versions

Priority

Created January 17, 2023 at 2:12 PM
Updated December 11, 2024 at 2:53 PM
Resolved November 29, 2024 at 3:29 PM