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

发布于 2024-08-15 08:22:54 字数 116 浏览 4 评论 0 原文

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

这是否仅仅意味着一个实体由于其所保存的数据的性质而具有的ID?

例如,用户名+密码+年龄+某些内容用作复合标识符?

While reading through the Hibernate documentation, I keep seeing references to the concept of a natural identifier.

Does this just mean the id an entity has due to the nature of the data it holds?

E.g. A user's name + password + age + something are used as a compound identitifier?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

_蜘蛛 2024-08-22 08:22:54

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

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

请参阅这篇优秀的博客文章以获得更详细的解释或这个RedHat 页面 获取示例 Hibernate 映射文件。

In Hibernate, natural keys are often used for lookups. You will have an auto-generated surrogate id in most cases. But this id is rather useless for lookups, as you'll always query by fields like name, social security number or anything else from the real world.

When using Hibernate's caching features, this difference is very important: If the cache is indexed by your primary key (surrogate id), there won't be any performance gain on lookups. That's why you can define a set of fields that you are going to query the database with - the natural id. Hibernate can then index the data by your natural key and improve the lookup performance.

See this excellent blog post for a more detailed explanation or this RedHat page for an example Hibernate mapping file.

递刀给你 2024-08-22 08:22:54

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

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

代理键如此受欢迎的原因是,与非常长的自然密钥(例如,VIN 需要 17 个字母数字字符,书籍 ISBN 是 13 位数字)相比,它们更紧凑(4 字节或 8 字节)。如果代理键成为主键,您可以使用 JPA @Id 注释来映射它。

现在,假设我们有以下 Post 实体:

使用自然 ID 发布实体

自 < code>Post 实体也有一个自然键,除了代理项之外,您还可以使用 Hibernate 特定的 @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);
    }
}

现在,考虑到上面的实体,用户可能会已为 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 实体数据库。很酷,对吧?

In a relational database system, typically, you can have two types of simple identifiers:

  • Natural keys, which are assigned by external systems and guaranteed to be unique
  • Surrogate keys, like IDENTITY or SEQUENCE which are assigned by the database.

The reason why Surrogate Keys are so popular is that they are more compact (4 bytes or 8 bytes), compared to a Natural Key which is very long (e.g. the VIN takes 17 alphanumerical characters, the book ISBN is 13 digits long). If the Surrogate Key becomes the Primary Key, you can map it using the JPA @Id annotation.

Now, let's assume we have the following Post entity:

Post entity with natural id

Since the Post entity that has also a Natural Key, besides the Surrogate one, you can 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);
    }
}

Now, considering the entity above, the user might have bookmarked a Post article and now they want to read it. However, the bookmarked URL contains the slug Natural Identifier, not the Primary Key.

So, we can fetch it like this using Hibernate:

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

Hibernate 5.5 or newer

When fetching the entity by its natural key on Hibernate 5.5 or newer, the following SQL query is generated:

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'

So, since Hibernate 5.5, the entity is fetched by its natural identifier directly from the database.

Hibernate 5.4 or older

When fetching the entity by its natural key on Hibernate 5.4 or older, two SQL queries are generated:

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

The first query is needed to resolve the entity identifier associated with the provided natural identifier.

The second query is optional if the entity is already loaded in the first or the second-level cache.

The reason for having the first query is because Hibernate already has a well-established logic for loading and associating entities by their identifier in the Persistence Context.

Now, if you want to skip the entity identifier query, you can easily annotate the entity using the @NaturalIdCache annotation:

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

This way, you can fetch the Post entity without even hitting the database. Cool, right?

半窗疏影 2024-08-22 08:22:54

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

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

A natural identifier is something that is used in the real world as an identifier. An example is a social security number, or a passport number.

It is usually a bad idea to use natural identifiers as keys in a persistence layer because a) they can be changed outside of your control, and b) they can end up not being unique due to a mistake elsewhere, and then your data model can't handle it so your application blows up.

怎言笑 2024-08-22 08:22:54

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

但是,长的可变长度字符串不是理想的键,因此您可能需要定义一个 代理 ID

又名 自然密钥 在关系设计中

What naturally identifies an entity. For example, my email address.

However, a long variable length string is not an ideal key, so you may want to define a surrogate id

AKA Natural key in relational design

硬不硬你别怂 2024-08-22 08:22:54

社会安全号码可能是自然身份,或者正如您所说的哈希值用户的信息。另一种方法是代理键,例如 Guid/UID。

A social security number might be a natural identity, or as you've said a hash of the User's information. The alternative is a surrogate key, for example a Guid/UID.

享受孤独 2024-08-22 08:22:54

在关系数据库理论中,一个关系可以有多个候选键。
候选键是关系的一组属性,在该关系的两行中永远不会重复关系,并且不能通过删除其中一个属性来减少,并且仍然保证唯一性。

自然 ID 本质上是一个候选键。其中“自然”意味着它是您在该关系中保存的数据的本质,而不是您添加的东西(例如自动生成的密钥)。
自然 ID 可以由单个属性组成。
一般来说,关系的任何唯一且非空的属性都是候选键,并且可以被视为自然 ID。

在 Hibernate 中,此注释可以简单地用于表示属性可用于执行搜索,在不使用键的情况下返回唯一结果。
当您表示为自然 id 的属性更适合您处理时(例如,当实际密钥是自动生成的并且您不想在搜索中使用时),这可能很有用。

In relational database theory a relation can have multiple candidate keys.
A candidate key is a set of attributes of a relation that are never duplicate in two rows of that relation and that cannot be reduced by removing one of the attributes and still guarantee uniqueness.

A natural ID is essentially a candidate key. Where "natural" means it is in the nature of the data you hold in that relation, not something you add like an autogenerated key.
A natural ID can be composed of a single attribute.
In general any attribute of a relation that is unique and not-null is a candidate key, and can be considered a natural id.

In Hibernate this annotation can be used simply to denote that an attribute can be used to do searches that return unique results while not using the key.
This can be useful when the attribute you denote as natural id is more natural to deal with for you, e.g. when the actual key is autogenerated and you don't want to use in searches.

醉生梦死 2024-08-22 08:22:54

自然标识符(也称为业务密钥):是表示或代表现实生活中某事物的标识符。

电子邮件国民身份证
Isbn 代表书籍
银行帐户的IBAN

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

Natural identifier (also known as business key): is an identifier that means or represent something in real life.

Email or national id for person
Isbn for Book
IBAN for Bank account

This @NaturalId Annotation is used to specify Natural identifier.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文