始终使用JPA @Id的原始对象包装器而不是原始类型?

我发现使用原始类型作为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.merge()

这里有趣的部分是Spring如何检查实体是否是新的:

 getId(entity) == null; 

当然,这是错误的,因为我使用long作为@Id的类型,而long的默认值是0.当我将long更改为Long时,一切都适用于Spring Data JPA。

因此,建议的做法总是使用对象包装器来处理基本类型(如Long而不是long)而不是基本类型。 将此描述为推荐做法的任何第三方资源都非常好。

我会说是的,建议使用对象类型而不是原语,因为你看到的情况。 没有办法区分实体是新的还是预先存在的原始标识符。 我已经使用了多年的hibernate,我总是使用对象作为标识符。

我会使用对象类型。 在xml映射中,您可以设置“unsaved-value”属性,但我认为没有直接转换为此注释。 因此,坚持使用对象类型会更安全。

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