在阅读Hibernate文档时,我一直看到对自然标识符这个概念的引用。
这是否意味着实体由于其所持有的数据的本质而具有的id?
例如,用户的姓名+密码+年龄+某些内容作为复合标识符使用?
在阅读Hibernate文档时,我一直看到对自然标识符这个概念的引用。
这是否意味着实体由于其所持有的数据的本质而具有的id?
例如,用户的姓名+密码+年龄+某些内容作为复合标识符使用?
在关系型数据库系统中,通常可以有两种类型的简单标识符:
IDENTITY
或 SEQUENCE
),由数据库分配。代理键之所以如此流行,是因为它们更紧凑(4字节或8字节),而自然键非常长(例如,车架号需要17个字符,书籍ISBN是13位数字)。如果将代理键作为主键,则可以使用JPA @Id
注释进行映射。
现在,假设我们有以下Post
实体:
由于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或更高版本中,通过其自然键获取实体时,将生成以下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或更早版本中,通过其自然键检索实体时会生成两个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
实体。很酷,对吧?
自然标识是指在现实世界中用作标识符的东西。例如,社会安全号码或护照号码。
通常情况下,在持久层中使用自然标识作为键是一个不好的想法,因为a)它们可能会在你无法控制的情况下被更改,b)由于其他地方的错误而导致它们不唯一,这样你的数据模型就无法处理,从而导致应用程序崩溃。
这个@NaturalId注释用于指定自然标识符。
@NaturalId
时,我们是否必须添加@NaturalIdCache
注释? 2. 就我所见,@NaturalId
注释用作索引,但我不确定在实体中有唯一字段时是否应该使用它。 有什么想法吗? 3. 当属性可更新或不可更新时,我可以使用@NaturalId
吗? - user19601508