Использование Hibernate последовательности PostgreSQL не влияет на таблицу последовательности

Я настроил Hibernate для использования последовательности PostgreSQL (через аннотации) для генерации значений для первичного ключа id следующим образом:

@Id 
@SequenceGenerator(name="pk_sequence",sequenceName="entity_id_seq")
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="pk_sequence")
@Column(name="id", unique=true, nullable=false)
public int getId() {
    return this.id;
}

То, что я вижу в этой конфигурации, - это то, что hibernate уже присваивает значения id > 3000 при сохранении, тогда как запрос на используемую последовательность показывает следующее:

database=# select last_value from entity_id_seq;
last_value 
------------
     69

(1 строка)

Вопросы:
Что-то не так или нет?
Должна ли синхронизация спящего режима с таблицей последовательностей?
Если нет, то где он хранит последний сгенерированный идентификатор?

Спасибо.

Ответ 1

У меня была та же проблема. Это связано с стратегиями выделения идентификаторов Hibernate. Вы выбираете GenerationType.SEQUENCE, Hibernate использует стратегию HiLo, которая по умолчанию выделяет идентификаторы в блоках по 50. Таким образом, вы можете явно установить значение allocSize следующим образом:

@Id 
@SequenceGenerator(name="pk_sequence",sequenceName="entity_id_seq", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="pk_sequence")
@Column(name="id", unique=true, nullable=false)
public int getId() {
    return this.id;
}

Хотя, я также слышал мнения, что использование стратегии HiLo с allocSize = 1 не является хорошей практикой. Некоторые люди рекомендуют использовать GenerationType.AUTO вместо этого, когда вам приходится иметь дело с последовательностями, управляемыми базой данных.

Обновление: Я в конечном итоге перешел с allocSize = 1, и теперь все работает так, как я ожидаю. Мое приложение таково, что мне действительно не нужны блоки ID, так что YMMV.

Ответ 2

НЕ ИСПОЛЬЗУЙТЕ GenerationType.SEQUENCE для последовательностей Postgres!

Это совершенно противоречиво, но люди Hibernate полностью испортили это. Вы должны использовать GenerationType.AUTO или Hibernate снести ваши последовательности, если вам нужно перезагрузить/перестроить вашу БД. Это почти криминально небрежно, что они позволили бы этому коду входить в сборку производства, но команда Hibernate довольно известна своими бычьими позициями в сторону плоских неправильных позиций (например, посмотрите их позицию на LEFT JOINs).

Ответ 3

Сначала вы должны определить, какую версию Hibernate вы используете. В терминах версий с гибернатом-сердечником, 3.2 и далее вводили более последовательную поддержку генераторов id, особенно в отношении определенных в аннотациях. См. http://in.relation.to/Bloggers/New323HibernateIdentifierGenerators для обсуждения.

Далее 3.6 был введен параметр ('hibernate.id.new_generator_mappings'), который заставляет генераторы обсуждаться в этом блоге по умолчанию, обрабатываются JPA-аннотации. По умолчанию установлено значение false, потому что Hibernate должен поддерживать обратную совместимость со старыми версиями. Если вы хотите новое поведение (которое полностью рекомендуется), просто установите для этого параметра значение true.

Как обрабатывается GenerationType, зависит от того, какую версию вы используете, и имеет ли значение hibernate.id.new_generator_mappings значение true. Предполагаю, что вы используете 3,6+ (так как что-то более старое, ну, старое) и имеют значение "hibernate.id.new_generator_mappings" равным true (так как это рекомендация для новых приложений):

  • GenerationType.AUTO → рассматривается как GenerationType.SEQUENCE
  • GenerationType.SEQUENCE → отображает класс org.hibernate.id.enhanced.SequenceStyleGenerator, обсуждаемый в блоге.
  • GenerationType.TABLE → сопоставляется с классом org.hibernate.id.enhanced.TableGenerator, обсуждаемым в блоге.

Ответ 4

В Postgres я бы сделал следующее:

@Id 
@SequenceGenerator(name="pk_sequence",sequenceName="\"entity_id_seq\"")
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="\"pk_sequence\"")
@Column(name="\"id\"", unique=true)
private int id;

В основном с именами верхнего регистра Hibernate необходимо передать экранированные кавычки, чтобы понять Postgres и найти имена таблиц, столбцов или последовательностей.