Подтвердить что ты не робот

Что такое естественный идентификатор в Hibernate?

Во время чтения документации Hibernate я продолжаю видеть ссылки на концепцию естественного идентификатора.

Означает ли это только, что идентификатор имеет сущность из-за характера данных, которые он хранит?

например. В качестве составного идентификатора используется имя пользователя + пароль + возраст + что-то?

4b9b3361

Ответ 1

В Hibernate часто используются натружные ключи для поиска. В большинстве случаев у вас будет автоматически сгенерированный суррогатный идентификатор. Но этот идентификатор довольно бесполезен для поиска, поскольку вы всегда будете запрашивать поля, такие как имя, номер социального страхования или что-либо еще из реального мира.

При использовании функций кэширования Hibernate это различие очень важно: если кеш индексируется вашим первичным ключом (суррогатным идентификатором), прирост производительности при поиске не будет. Вот почему вы можете определить набор полей, которые вы собираетесь запрашивать в базе данных, с естественным идентификатором. Hibernate может затем индексировать данные по вашему естественному ключу и улучшать производительность поиска.

Смотрите это отличное сообщение для более подробного объяснения или RedHat страница для примера файла сопоставления Hibernate.

Ответ 2

Что естественно идентифицирует сущность. Например, мой адрес электронной почты.

Однако длинная строка переменной длины не является идеальным ключом, поэтому вы можете определить суррогатный идентификатор

AKA Естественный ключ в реляционном дизайне

Ответ 3

Естественный идентификатор - это то, что используется в реальном мире как идентификатор. Примером может служить номер социального страхования или номер паспорта.

Как правило, плохая идея использовать естественные идентификаторы в качестве ключей в уровне персистентности, потому что: а) они могут быть изменены вне вашего контроля, и б) они могут оказаться не уникальными из-за ошибки в другом месте, а затем ваши модель данных не может справиться с этим, чтобы ваше приложение взорвалось.

Ответ 5

В реляционной системе баз данных, как правило, вы можете два типа простых идентификаторов:

  • Натуральные ключи, которые назначаются внешними системами и гарантированно уникальны
  • Суррогатные ключи, такие как IDENTITY или SEQUENCE, которые назначаются базой данных.

Причина, по которой Surrogate Keys настолько популярна, состоит в том, что они более компактны (4 байта или 8 байтов) по сравнению с естественным ключом, который очень длинный (например, VIN занимает 17 буквенно-цифровых символов, книга ISBN составляет 13 цифр).

Теперь, если суррогатный ключ станет Первичным ключом, вы используете его с помощью аннотации 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);
    }
}

Теперь, рассматривая сущность выше, пользователь мог бы добавить в закладки статью Post, и теперь они хотят ее прочитать. Однако URL с закладкой содержит slug Natural Identifier, а не основной ключ.

Итак, мы можем получить его так, используя 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, даже не попав в базу данных. Прохладно, правильно?