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

Posted

技术标签:

【中文标题】始终为 JPA @Id 使用原始对象包装器而不是原始类型?【英文标题】:Always use primitive object wrappers for JPA @Id instead of primitive type? 【发布时间】:2012-07-02 06:42:04 【问题描述】:

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

【问题讨论】:

Primitive or wrapper for hibernate primary keys的可能重复 【参考方案1】:

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

而且大多数程序员都希望标识符中的“null”值表示未保存。

【讨论】:

【参考方案2】:

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

【讨论】:

以上是关于始终为 JPA @Id 使用原始对象包装器而不是原始类型?的主要内容,如果未能解决你的问题,请参考以下文章

Java 数据库 ID - 原始类型或包装类型 [重复]

Spring JPA 查询始终使用序列扫描而不是索引扫描

是否始终将 UUID 用于 JPA 实体的 id 属性并在其上使用等于和哈希码?

我是不是应该始终将 InputStream 包装为 BufferedInputStream?

python 使用对象和装饰器而不是仅使用一个方法和一些常量的类

原始类型包装器