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

o2g1uqev  于 2022-11-20  发布在  Java
关注(0)|答案(7)|浏览(168)

在阅读Hibernate文档时,我不断看到对 * 自然标识符 * 概念的引用。
这是否仅仅意味着一个实体由于其所持有的数据的性质而具有的id?
例如,使用用户名+密码+年龄+某项作为复合标识符?

jfgube3f

jfgube3f1#

在 Hibernate 中 , 自然 键 经常 被 用于 查找 。 在 大多 数 情况 下 , 你 会 有 一 个 自动 生成 的 代理 id 。 但是 这个 id 对于 查找 是 相当 无用 的 , 因为 你 总是 通过 像 姓名 、 社会 保险 号 或 其他 任何 来自 现实 世界 的 字段 来 查询 。
在 使用 Hibernate 的 缓存 特性 时 , 这种 差异 非常 重要 :如果 缓存 是 按 主键 ( 代理 id ) 索引 的 , 那么 在 查找 时 不会 有 任何 性能 提升 。 这 就是 为什么 你 可以 定义 一 组 字段 , 用 自然 id 来 查询 数据 库 。 然后 Hibernate 就 可以 按 自然 键 索引 数据 , 从而 提高 查找 性能 。
请 参阅 这个 优秀 的 blog post 以 获得 更 详细 的 说明 , 或者 参阅 这个 RedHat page 以 获得 Hibernate Map 文件 的 示例 。

hkmswyz6

hkmswyz62#

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

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

代理键如此受欢迎的原因是,与非常长的自然键(例如,VIN需要17个字母数字字符,图书ISBN为13位数)相比,代理键更紧凑(4字节或8字节)。如果代理键成为主键,则可以使用JPA @Id注解对其进行Map。
现在,让我们假设有以下Post实体:

由于Post实体除了Surrogate实体之外,还具有一个Natural Key,因此可以使用Hibernate特定的@NaturalId注解来Map它:

@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);
    }
}

现在,考虑到上面的实体,用户可能已经将Post文章加了书签,现在他们想阅读它。但是,加了书签的URL包含slug自然标识符,而不是主键。
因此,我们可以使用Hibernate像这样获取它:

Post post = entityManager.unwrap(Session.class)
.bySimpleNaturalId(Post.class)
.load(slug);

Hibernate 5.5或更高版本

在Hibernate 5.5或更高版本中按实体的自然键提取实体时,将生成以下SQL查询:

SELECT p.id AS id1_0_0_,
       p.slug AS slug2_0_0_,
       p.title AS title3_0_0_
FROM post p
WHERE p.slug = 'high-performance-java-persistence'

因此,从Hibernate 5.5开始,实体通过其自然标识符直接从数据库中获取。

Hibernate 5.4或更低版本

在Hibernate 5.4或更低版本中按实体的自然键提取实体时,将生成两个SQL查询:

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

需要第一查询来解析与所提供的自然标识符相关联的实体标识符。
如果实体已加载到第一级或第二级缓存中,则第二个查询是可选的。
使用第一个查询的原因是因为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);
    }
}

这样,您就可以获取Post实体,而无需访问数据库。

44u64gxh

44u64gxh3#

自然标识符是在真实的世界中用作标识符的东西。例如社会安全号码或护照号码。
在持久层中使用自然标识符作为键通常是个坏主意,因为a)它们可能会在您的控制之外被更改,b)它们可能会由于其他地方的错误而变得不唯一,然后您的数据模型无法处理它,从而导致您的应用程序崩溃。

mwkjh3gx

mwkjh3gx4#

实体的自然标识。例如,我的电子邮件地址。
但是,长的可变长度字符串不是理想的键,因此您可能需要定义一个代理id
关系设计中的AKA Natural key

prdp8dxp

prdp8dxp5#

社会安全号码可能是一个natural identity,或者如你所说的用户信息的哈希值,另一个是surrogate key,例如Guid/UID。

dldeef67

dldeef676#

在关系数据库理论中,一个关系可以有多个 * 候选键 *。candidate key是一个关系的一组属性,这些属性在该关系的两行中永远不会重复,并且不能通过删除其中一个属性来减少,但仍然保证唯一性。
自然ID本质上是候选键。其中“自然”表示它是您在该关系中保存的数据的性质,而不是您添加的东西,如自动生成的键。自然ID可以由单个属性组成。通常,关系的任何唯一且非空的属性都是候选键,并且可以被视为自然ID。
在Hibernate中,这个注解可以简单地用来表示一个属性可以用来执行搜索,返回唯一的结果,而不使用键。当你用自然id表示的属性对你来说更自然的时候,这会很有用,比如当实际的键是自动生成的,而你不想在搜索中使用的时候。

k4ymrczo

k4ymrczo7#

自然标识符(也称为业务密钥):是指或代表真实的生活中某事物的标识符。
电子邮件身份证号*******人
Isbn
银行帐户的IBAN

@NaturalId注解用于指定自然标识符。

相关问题