12

我发现将原始类型用作 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)使用对象包装器而不是原始类型。任何将此描述为推荐做法的第三方资源都会非常好。

4

2 回答 2

15

我会说是的,由于您所看到的情况,建议使用对象类型而不是原语。无法区分实体是新实体还是具有原始标识符的预先存在的实体。我已经使用休眠多年了,我总是使用对象作为标识符。

于 2012-07-01T16:59:14.973 回答
0

我会使用对象类型。在 xml 映射中,您可以放置​​一个“未保存的值”属性,但我认为没有直接翻译为此注释。因此,坚持使用对象类型更安全。

大多数程序员都希望标识符中的“空”值无论如何都意味着未保存。

于 2012-07-01T21:43:15.530 回答