始终使用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”属性,但我认为没有直接转换为此注释。 因此,坚持使用对象类型会更安全。
并且大多数程序员都希望标识符中的“空”值无论如何都意味着未保存。