我发现将原始类型用作 JPA 的对象 @Id 与 Spring Data JPA 结合使用的问题。我在父方与 Cascade.ALL 有父/子关系,并且子有 PK,同时也是父的 FK。
class Parent {
@Id
private long id;
@OneToOne(mappedBy = "parent", cascade = ALL)
private Child child;
}
class Child {
@Id
@OneToOne
private Parent parent;
}
所以,当我运行时:
...
Parent parent = new Parent();
Child child = new Child(parent);
parent.setChild(child);
em.persist(parent)
...
一切正常。但是我使用 Spring Data JPA 来持久化实体,所以我改为运行:
parentRepository.save(parent); // instead of em.persist(parent);
这个失败了,但有以下例外:
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Parent
问题是 Spring Data JPA save()方法检查实体是否是新的,如果是新的,则使用 em.persist (),否则使用em.merge()。
这里有趣的部分是 Spring 如何检查实体是否是新的:
getId(entity) == null;
而且,当然,这是错误的,因为我使用 long 作为@Id 的类型,而 long 的默认值为 0。当我将 long 更改为 Long 时,一切也适用于 Spring Data JPA。
因此,建议的做法是始终对原始类型(如 Long 而不是 long)使用对象包装器而不是原始类型。任何将此描述为推荐做法的第三方资源都会非常好。