我发现使用原始类型作为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()方法检查实体是否是新的,如果是new,则使用 em.persist() <使用em> em.merge()。
这里有趣的部分是Spring如何检查实体是否是新的:
getId(entity) == null;
当然,这是假的,因为我使用long作为@Id的类型,而long的默认值是0.当我将long改为Long时,一切都适用于Spring Data JPA。
因此,建议的做法总是使用对象包装器来处理基本类型(如Long而不是long)而不是基本类型。任何描述这个作为推荐做法的第三方资源都会非常好。
答案 0 :(得分:14)
我会说是的,因为你看到的情况,建议使用对象类型而不是基元。没有办法区分实体是新的还是预先存在的原始标识符。我已经使用了多年的hibernate,我总是使用对象作为标识符。
答案 1 :(得分:0)
我会使用对象类型。在xml映射中,您可以设置“unsaved-value”属性,但我认为没有直接转换为此注释。因此,坚持使用对象类型会更安全。
大多数程序员都希望标识符中的“null”值无论如何都意味着未保存。