ben*_*rre 19 java orm hibernate jpa natural-key
在阅读Hibernate文档时,我不断看到对自然标识符概念的引用.
这是否仅仅意味着实体所具有的id由于其所拥有的数据的性质?
例如,用户的姓名+密码+年龄+某些东西被用作复合识别符?
自然标识符是在现实世界中用作标识符的东西.一个例子是社会安全号码或护照号码.
使用自然标识符作为持久层中的键通常是一个坏主意,因为a)它们可以在您的控件之外进行更改,并且b)由于其他地方的错误,它们最终可能不会是唯一的,然后您的数据模型可以处理它,以便您的应用程序爆炸.
通常,在关系数据库系统中,您可以具有两种类型的简单标识符:
代理键之所以如此受欢迎,是因为与非常长的自然键(例如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甚至无需访问数据库就可以获取实体。酷吧?