Reputation: 8305
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.
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
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.
What is the root reason of this issue? Is @IdClass not supposed to be used for this scenario?
https://github.com/spring-projects/spring-data-jpa/issues/2767
https://hibernate.atlassian.net/browse/HHH-16054
Upvotes: 0
Views: 303
Reputation: 16400
You have to assign your local customer variables to the object that is returned by repository.save()
, since internally it calls EntityManager.merge()
which kind of mandates this.
Upvotes: 0