В системе реляционных баз данных, как правило, вы можете иметь два типа простых идентификаторов :
- Натуральные ключи, которые присваиваются внешними системами и гарантированно являются уникальными
- Суррогатные ключи, такие как IDENTITY или SEQUENCE , которые назначаются базой данных.
Причина, по которой суррогатные ключи так популярны, заключается в том, что они более компактны (4 байта или 8 байт) по сравнению с натуральным ключом, который очень длинный (например, VIN занимает 17 буквенно-цифровых символов, книга ISBN имеет длину 13 цифр). ).
Теперь, если суррогатный ключ становится первичным ключом, вы можете сделать это, используя аннотацию JPA @Id
.
И, если у вас есть сущность, имеющая также Природный Ключ, помимо Суррогатного, вы можете сопоставить его с привязкой к 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 выполнит следующие два запроса:
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
, даже не обращаясь к базе данных. Круто, правда?