Stumped, utterly stumped...
Assume two entities, Parent and Child, with many Child entities to one Parent. Parent's primary key is of type java.util.UUID
, and Child's primary key is a composite of the Parent's UUID and a sequence number.
The short of the issue is when I try to save a new Child using childRepository.save(child)
, I get the following exception:
Caused by: java.lang.IllegalArgumentException: Cannot convert value of type [com.package.entities.ParentEntity$$_jvst149_0] to required type [java.util.UUID] for property 'parent': PropertyEditor [org.springframework.beans.propertyeditors.UUIDEditor] returned inappropriate value of type [com.package.entities.ParentEntity_$$_jvst149_0]
Please look at my classes below. The best I can tell I am following the JPA
spec correctly, so I'm wondering if this is a bug in Spring Data JPA
, perhaps specific to UUID type IDs (similar thing has happened before, see DATAJPA-269)
Note I am using spring-boot-starter-data-jpa 1.4.1.RELEASE
Parent.java:
@Entity
@Table(name = "parent")
public class Parent implements Serializable {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private UUID id;
//...other fields, getters + setters...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Parent that = (Parent) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Child.java
@Entity
@Table(name = "child")
@IdClass(ChildKey.class)
public class Child implements Serializable {
@Id
@ManyToOne
@JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
private Parent parent;
@Id
private Integer seqNum;
//...other fields, getters + setters...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Child that = (Child) o;
return Objects.equals(parent, that.parent) &&
Objects.equals(seqNum, that.seqNum);
}
@Override
public int hashCode() {
return Objects.hash(parent, seqNum);
}
}
ChildKey.class
public class ChildKey implements Serializable {
private UUID parent;
private Integer seqNum;
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ChildKey that = (ChildKey) o;
return Objects.equals(parent, that.parent) &&
Objects.equals(seqNum, that.seqNum);
}
@Override
public int hashCode() {
return Objects.hash(parent, seqNum);
}
}
ParentRepository.java
@Repository
public interface ParentRepository extends JpaRepository<Parent, UUID> {
}
ChildRepository.java
@Repository
public interface ChildRepository extends CrudRepository<Child, ChildKey> {
}
And finally, the code I execute:
@Transactional
public void createChild(Parent parent) {
// needed to do this to get over "detached entity passed to persist"
parent = parentRepository.getOne(parent.getId());
child = new Child();
child.setParent(parent);
child.setSeqNum(1);
childRepository.save(child);
}
In Many-To-One relationship your child entity has it's own ID, and ID from parent entity is FK not a part of PK. Example