Spring Data JPA:无法使用包含外键的复合主键保存实体



被绊倒了,完全被绊倒了。。。

假设两个实体,父实体和子实体,其中一个父实体具有多个子实体。父级的主键类型为java.util.UUID,子级的主键是父级UUID和序列号的组合。

简而言之,当我尝试使用childRepository.save(child)保存一个新的Child时,我会得到以下异常:

由以下原因引起:java.lang.IollegalArgumentException:无法转换的值键入[com.package.entitys.ParentEntity$$_jvst149_0]为所需类型属性'parent'的[java.util.UUID]:PropertyEditor[org.springframework.beans.propertyeditors.UUIDEditor]返回类型值不合适[com.package.entitys.ParentEntity_$$_jvst149_0]

请看下面的课程。据我所知,我正确地遵循了JPA规范,所以我想知道这是否是Spring Data JPA中的一个错误,可能是UUID类型ID特有的(类似的事情以前也发生过,请参阅DATAJPA-269)

注意:我使用的是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> {
}

最后,我执行的代码:

@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);
}

在多对一关系中,子实体有自己的ID,而父实体的ID是FK,而不是PK的一部分。示例

自从我发布这个问题以来的几个月里,我一直没有找到合适的答案。不幸的是,我不得不通过不使用@ManyToOne来解决这个问题,而只是通过UUID:引用父级

public class Child implements Serializable {
@Id
private UUID parentId;
@Id
private Integer seqNum;

我让JPA不知道外键,如果我违反引用完整性,就让数据库抛出一个错误。

您需要更改ChildKey类:

public class ChildKey implements Serializable {
private Parent parent; // <-- Parent type instead of UUID
private Integer seqNum;
...

UPD:我阅读了JPA规范,并理解它是不正确的。但这对我来说是有效的。

最新更新