Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Идентификаторы

Каждой сущности нужен первичный ключ — идентификатор, уникальный в пределах таблицы. Обычно программа его не придумывает: его генерирует база данных или Hibernate. Как именно, задаёт параметр strategy аннотации @GeneratedValue, и от выбора зависит, когда выполняется insert.

Стратегии

СтратегияКто создаёт значениеСтолбец
AUTO (по умолчанию)Выбирает Hibernate; для H2, PostgreSQL и Oracle это последовательностьbigint
SEQUENCEПоследовательность (sequence) в базеbigint
IDENTITYБаза, как столбец с автоувеличениемbigint generated by default as identity
UUIDHibernate, случайный UUIDuuid

Три сущности, три стратегии

// src/main/java/com/example/blog/Post.java
@Entity
public class Post {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    private String title;
    // конструкторы и геттеры
}
// src/main/java/com/example/blog/Comment.java
@Entity
public class Comment {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = "comment_seq", allocationSize = 10)
    private Long id;
 
    private String text;
    // конструкторы и геттеры
}
// src/main/java/com/example/blog/Tag.java
@Entity
public class Tag {
 
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;
 
    private String name;
    // конструкторы и геттеры
}

Аннотации импортируются из jakarta.persistence, UUID — из java.util. Программа сохраняет одну статью, три комментария и тег и печатает их id сразу после persist:

Hibernate: create sequence comment_seq start with 1 increment by 10
Hibernate: create table Comment (id bigint not null, text varchar(255), primary key (id))
Hibernate: create table Post (id bigint generated by default as identity, title varchar(255), primary key (id))
Hibernate: create table Tag (id uuid not null, name varchar(255), primary key (id))
Hibernate: insert into Post (title,id) values (?,default)
id статьи: 1
Hibernate: select next value for comment_seq
id комментария: 1
Hibernate: select next value for comment_seq
id комментария: 2
id комментария: 3
id тега: 0807f8ef-7f04-4dfe-b207-c3e9f3ff3bfa
Hibernate: insert into Comment (text,id) values (?,?)
Hibernate: insert into Comment (text,id) values (?,?)
Hibernate: insert into Comment (text,id) values (?,?)
Hibernate: insert into Tag (name,id) values (?,?)

Что показывает вывод

  • IDENTITY: id становится известен только после вставки строки, поэтому Hibernate выполняет insert сразу при persist. Из-за этого он не может объединять вставки в пакеты.
  • SEQUENCE: Hibernate берёт значения из последовательности заранее. С allocationSize = 10 одно обращение к базе резервирует десять id, поэтому третий комментарий получил id без всякого запроса. Команды insert ждут фиксации. По умолчанию allocationSize равен 50 — это и есть increment by 50, который вы видели раньше.
  • UUID: значение создаётся в Java, база в этом не участвует.

Для новых проектов последовательность по умолчанию — хороший выбор. UUID полезен, когда id создаются в нескольких местах или их нельзя угадывать.

Натуральные идентификаторы

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

// src/main/java/com/example/blog/Post.java
@Id
@GeneratedValue
private Long id;
 
@NaturalId
private String slug;

@NaturalId импортируется из org.hibernate.annotations и добавляет ограничение уникальности. По нему можно загрузить статью:

// src/main/java/com/example/blog/Main.java
Post post = emf.callInTransaction(em ->
        em.find(Post.class, "hello-hibernate", KeyType.NATURAL));

Запрос будет таким: select ... from Post p1_0 where p1_0.slug=?. KeyType находится в пакете org.hibernate.

Частая ошибка

Если у сущности есть @GeneratedValue, не задавайте id сами. Hibernate видит заполненный id и решает, что объект уже есть в базе:

Exception in thread "main" jakarta.persistence.EntityExistsException: Detached entity passed to persist: com.example.blog.Post

Либо доверьте генерацию Hibernate, либо уберите @GeneratedValue и всегда назначайте id сами.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.