Странные значения id в Postgresql с генерацией последовательности JPA 2 - PullRequest
4 голосов
/ 15 июня 2011

У сущности есть следующие аннотации в столбце id:

@Id
@SequenceGenerator(name = "JOB_MISFIRE_ID_GENERATOR", sequenceName="job_misfire_sequence", allocationSize=10)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "JOB_MISFIRE_ID_GENERATOR")
@Column(unique = true, nullable = false)
private Long id;

В базе данных у меня есть следующее:

CREATE SEQUENCE job_misfire_sequence
  INCREMENT 10
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 1;

И эта последовательность используется для получения значения по умолчанию для столбца.

ALTER TABLE job_misfires
ALTER COLUMN id SET DEFAULT nextval('job_misfire_sequence');

Когда я выполняю ручную вставку вручную в базу данных, используя nextval ('job_misfire_sequence'), все работает хорошо. Когда текущее значение последовательности было 1, были получены следующие значения идентификатора:

 SELECT nextval('job_misfire_sequence'); --> 1
 SELECT nextval('job_misfire_sequence'); --> 11
 SELECT nextval('job_misfire_sequence'); --> 21
 SELECT nextval('job_misfire_sequence'); --> 31

Но что происходит, когда hibernate вставляет строку в эту таблицу, так это то, что она получает следующее значение из этой последовательности (в данном сценарии - 41), умножает его на 10 и использует это значение id. Это означает, что вставленная строка теперь имеет значение идентификатора 410.

Что я делаю не так? Эта ситуация приведет к конфликтам, так как hibernate не использует значение, предоставленное последовательностью. Если я понял, правильное сочетание
allocationSize = 10 в аннотации и INCREMENT 10 в последовательности следует гарантировать, что hibernate должен запрашивать новое значение из последовательности каждое десятое значение. Почему этого не происходит? Почему значение из последовательности умножается на 10?

Я использую

  • Postgresql 9.0.3
  • Hibernate 3.5.5
  • Hibernate JPA 2.0 api 1.0.0 final

Обновление 1:

Как было предложено в интернете, установка значения allocSize в аннотации 1 решает эту проблему. Теперь значения идентификаторов действительно взяты из последовательности в db, и я могу спокойно вставлять строки в эту таблицу вручную.

Но:

  • Вызывает ли наличие allocSize = 1 проблемы с производительностью?
  • Разве это не огромная ошибка в том, что значение из последовательности не используется, как в hibernate, а умножается на значение allocSize?
  • Кто виноват? Hibernate?
  • Есть ли исправление?

Ответы [ 4 ]

7 голосов
/ 15 июня 2011

Кажется, что правильный способ сделать это заключается в следующем:

@Id
@SequenceGenerator(name = "JOB_MISFIRE_ID_GENERATOR", sequenceName="job_misfire_sequence", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "JOB_MISFIRE_ID_GENERATOR")
@Column(unique = true, nullable = false)
private Long id;

В базе данных у меня есть следующее:

CREATE SEQUENCE job_misfire_sequence
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 10;

Теперь, когда я выполняю ручную вставку вручную в базу данных, используя nextval ('job_misfire_sequence'), все работает как положено. Когда текущее значение последовательности было 1, были получены следующие значения идентификатора:

 SELECT nextval('job_misfire_sequence'); --> 1
 SELECT nextval('job_misfire_sequence'); --> 2
 SELECT nextval('job_misfire_sequence'); --> 3
 SELECT nextval('job_misfire_sequence'); --> 4

Теперь hibernate также работает так, как я ожидаю. Когда он вставляет строку после того, как я вставил эти 4 строки в один сеанс, значение последовательности, возвращаемое в hibernate, равно 11. Hibernate использует это в качестве значения id для первой записи, 11 для следующей и так далее. Поскольку я установил для параметра CACHE значение 10 в дБ, hibernate теперь должен вызывать последовательность только один раз, а затем может использовать 10 последовательных значений идентификатора. Я подтвердил, что это действительно так и что значения идентификаторов не перекрываются.

Итак, ключевые моменты:

  • Вы должны использовать allocSize = 1 в аннотации

Если вы хотите оптимизировать производительность вставок БД, используйте

  • Используйте настройку CACHE в дБ со значением больше 1, но НЕ трогайте allocationSize

Для получения хороших последовательных значений идентификатора

  • Вы должны использовать INCREMENT 1
0 голосов
/ 15 июня 2011

Я не знаю Hibernate, но когда последовательность создается со значением кэша в PostgreSQL, этот кэш работает для каждого соединения отдельно.Это означает, что если вы вызываете nextval () из разных сеансов (= соединений), вы также можете увидеть это поведение.

Цитата из руководства:

Могут быть получены неожиданные результатыесли параметр кэша больше единицы используется для объекта последовательности, который будет использоваться одновременно несколькими сеансами.Каждый сеанс будет распределять и кэшировать последовательные значения последовательности во время одного доступа к объекту последовательности и соответственно увеличивать last_value объекта последовательности.Затем следующее использование кэша-1 в следующем сеансе nextval просто возвращает предварительно выделенные значения, не касаясь объекта последовательности.Таким образом, любые номера, выделенные, но не использованные в сеансе, будут потеряны по окончании этого сеанса, что приведет к появлению «дыр» в последовательности

. Обязательно прочитайте раздел «Примечания» в руководстве: http://www.postgresql.org/docs/current/static/sql-createsequence.html

0 голосов
/ 15 июня 2011

Вот как это работает.

При использовании генератора последовательности с allocationSize Hibernate получает одно число из последовательности для генерации allocationSize идентификаторов. Таким образом, после получения значения N из последовательности генерируются идентификаторы от allocationSize * N до allocationSize * (N + 1) - 1. Затем он получает следующее значение из последовательности, чтобы сгенерировать следующую группу идентификаторов. Если это следующее значение N + 1, сгенерированные идентификаторы являются непрерывными, так что он ожидает seqeunce, который производит последовательные числа.

Таким образом, нет необходимости указывать increment в определении последовательности.

0 голосов
/ 15 июня 2011

Как я понимаю, nextVal('job_misfire_sequence'); вернет вам следующее значение SEQUENCE, то есть значение, которое вам нужно.Hibernate может абстрагироваться от этого и предположить, что значение, возвращаемое из БД, является правильным.Таким образом, вам не нужно allocationSize=10, потому что база данных уже возвращает правильное значение.

...