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

时间:2009-12-15 20:37:29

标签: java hibernate jpa orm natural-key

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

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

E.g。用户名+密码+年龄+某些东西用作复合识别符?

7 个答案:

答案 0 :(得分:19)

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

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

有关更详细的说明,请参阅此优秀blog post,或查看示例Hibernate映射文件的此RedHat page

答案 1 :(得分:8)

自然识别实体的内容。例如,我的电子邮件地址。

但是,长可变长度字符串不是理想的键,因此您可能需要定义surrogate id

关键设计中的

AKA Natural key

答案 2 :(得分:5)

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

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

答案 3 :(得分:5)

在关系数据库系统中,通常可以使用two types of simple identifiers

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

代理键如此受欢迎的原因是它们更紧凑(4字节或8字节),而自然键非常长(例如VIN需要17个字母数字字符,书籍ISBN长度为13位) )。

现在,如果代理键成为主键,则使用JPA @Id注释来填充它。

而且,如果你有一个也有自然键的实体,除了代理者之外,你可以map it with the Hibernate-specific @NaturalId annotation

@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将执行以下两个查询:

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

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

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

正如我在this article中解释的那样,获得第一个查询的原因是因为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实体。很酷,对吧?

答案 4 :(得分:2)

社会安全号码可能是natural identity,或者您已经说过用户信息的哈希值。替代方案是surrogate key,例如Guid / UID。

答案 5 :(得分:1)

自然标识符(也称为业务密钥):是表示或代表现实生活中的某些事物的标识符。
电子邮件国家/地区ID 图书 Isbn
银行帐户

IBAN

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

答案 6 :(得分:1)

在关系数据库理论中,一个关系可以具有多个候选键candidate key是关系的一组属性,这些属性从不在该关系的两行中重复,并且不能通过删除其中一个属性而仍可保证唯一性而减少。

自然ID本质上是候选密钥。 “自然”是指您在该关系中拥有的数据的本质,而不是像自动生成的键那样添加的东西。 自然ID可以由单个属性组成。 通常,关系的唯一且非空的任何属性都是候选键,可以视为自然ID。

在Hibernate中,此注释可以仅用于表示属性可以用于执行搜索,这些搜索在不使用键的情况下返回唯一的结果。 当您将表示为自然ID的属性更自然地处理时,例如,当自动生成实际密钥并且您不想在搜索中使用时。