Hibernate中的自然标识符是什么?

ben*_*rre 19 java orm hibernate jpa natural-key

在阅读Hibernate文档时,我不断看到对自然标识符概念的引用.

这是否仅仅意味着实体所具有的id由于其所拥有的数据的性质?

例如,用户的姓名+密码+年龄+某些东西被用作复合识别符?

chr*_*ris 19

在Hibernate中,自然密钥通常用于查找.在大多数情况下,您将拥有自动生成的代理ID.但是这个id对于查找来说是无用的,因为你总是会查询名称,社会安全号码或现实世界中的任何其他字段.

使用Hibernate的缓存功能时,这种差异非常重要:如果缓存由主键(代理ID)索引,则查找不会有任何性能提升.这就是为什么您可以定义一组要用数据库查询数据库的字段 - 自然ID.然后,Hibernate可以通过自然键索引数据并提高查找性能.

有关示例Hibernate映射文件的详细说明,请参阅此优秀博客文章或此RedHat页面.


gbn*_*gbn 8

什么自然地标识一个实体.例如,我的电子邮件地址.

但是,长可变长度字符串不是理想的键,因此您可能希望定义代理ID

AKA 关系设计中的自然关键


Mar*_*ers 6

自然标识符是在现实世界中用作标识符的东西.一个例子是社会安全号码或护照号码.

使用自然标识符作为持久层中的键通常是一个坏主意,因为a)它们可以在您的控件之外进行更改,并且b)由于其他地方的错误,它们最终可能不会是唯一的,然后您的数据模型可以处理它,以便您的应用程序爆炸.

  • 你可以在你的数据模型中限制它,但你不能限制现实生活 - 错误确实会发生,并且你的数据模型在它们发生时不需要破坏。如果您需要更正某人的 SSN,例如因为输入错误,则应该是一次更新。如果您在整个系统中将其用作密钥……将其序列化,将其存储在备份中,甚至可能将其发送到外部系统,那么您就完蛋了。您不可能在不破坏某些内容的情况下更新该人的 SSN。PS:除非必须,否则根本不要存储 SSN。 (2认同)

Vla*_*cea 5

通常,在关系数据库系统中,您可以具有两种类型的简单标识符

  • 自然键,由外部系统分配,并保证是唯一的
  • 由数据库分配的代理键,例如IDENTITY或SEQUENCE

代理键之所以如此受欢迎,是因为与非常长的自然键(例如VIN使用17个字母数字字符,书号ISBN为13位数字)相比,它们更紧凑(4字节或8字节)。

现在,如果代理键成为主键,则可以使用JPA @Id批注来代替它。

而且,如果您拥有一个具有自然键的实体,那么除了代理对象外,还可以使用特定于休眠的@NaturalId注释进行映射

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @NaturalId
    @Column(nullable = false, unique = true)
    private String slug;

    //Getters and setters omitted for brevity

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) 
            return false;
        Post post = (Post) o;
        return Objects.equals(slug, post.slug);
    }

    @Override
    public int hashCode() {
        return Objects.hash(slug);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,考虑到上面的实体,用户可能已经为Post文章添加了书签,现在他们想阅读它。但是,加书签的URL包含slug自然标识符,而不是主键。

因此,我们可以使用Hibernate这样获取它:

Post post = entityManager.unwrap(Session.class)
.bySimpleNaturalId(Post.class)
.load(slug); 
Run Code Online (Sandbox Code Playgroud)

Hibernate将执行以下两个查询:

SELECT p.id AS id1_0_
FROM post p
WHERE p.slug = 'high-performance-java-persistence'

SELECT p.id AS id1_0_0_,
       p.slug AS slug2_0_0_,
       p.title AS title3_0_0_
FROM post p
WHERE p.id = 1
Run Code Online (Sandbox Code Playgroud)

需要第一查询来解析与所提供的自然标识符相关联的实体标识符。

如果实体已经加载到第一级或第二级缓存中,则第二个查询是可选的。

正如我在本文中所解释的,进行第一个查询的原因是因为Hibernate已经具有完善的逻辑,用于在持久性上下文中通过其标识符加载和关联实体。

现在,如果要跳过实体标识符查询,则可以使用@NaturalIdCache注释轻松地为实体添加注释:

@Entity(name = "Post")
@Table(name = "post")
@org.hibernate.annotations.Cache(
    usage = CacheConcurrencyStrategy.READ_WRITE
)
@NaturalIdCache
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @NaturalId
    @Column(nullable = false, unique = true)
    private String slug;

    //Getters and setters omitted for brevity

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) 
            return false;
        Post post = (Post) o;
        return Objects.equals(slug, post.slug);
    }

    @Override
    public int hashCode() {
        return Objects.hash(slug);
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,您Post甚至无需访问数据库就可以获取实体。酷吧?