Различные идентификаторы последовательностей PostgreSQL в базе данных и в JPA - PullRequest
3 голосов
/ 16 февраля 2011

Я искренне озадачен ... но сначала позвольте мне дать вам приблизительный обзор.

Я провел некоторую реструктуризацию в базе данных, объединив 4 таблицы в две.Все таблицы имеют простые числовые последовательности в качестве первичных ключей.Таблицы были в парах очень (очень) похожи.Единственная причина, почему они были разделены на две части, была основана на исторических данных, которые пришлось импортировать.Без этого разделения было бы много избыточности, и концептуально это имело смысл.

Теперь, после того, как большая часть работы ушла на очистку данных, теперь наконец-то можно объединить их и просто использовать один изполя как дискриминатор.Говоря менее абстрактно, таблицы содержат компании.И они либо местные жители, либо нет (два класса).Их легко отличить по почтовому индексу (поле дискриминатора).Эти таблицы являются медленно меняющимися измерениями (последовательность является суррогатным ключом).Две другие таблицы содержат нормальные данные, прикрепленные к этим SCD.Отсюда 4 таблицы.2 для местных компаний и 2 для не местных жителей.

Эти таблицы теперь упрощены и объединены, поэтому у меня теперь есть только Company и CompanyData.

.безопасная сторона, и чтобы не потерять какую-либо историческую информацию, я создал две новые таблицы с новыми полями последовательности.Старые последовательности хранятся на случай, если через 10 лет я пойму, что что-то пошло не так;)

Пока все хорошо.

Реструктуризация была довольно простой, воссоединение правильных записей было не сложнотакже.Затем мне нужно было обновить приложение, которое взаимодействует с этой БД, что было немного больше работы, но все же легко.Приложение использует JPA с использованием EclipseLink 2.0 поверх - как уже говорилось - базы данных PostgreSQL 9.0.

И вот странная часть:

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

Так что я немного покопался.Я мог проверить, что последующие инерты действительно возвращали дублирующиеся ошибки ключа с увеличивающими идентификаторами.Это означает, что логика последовательности в порядке.Единственная проблема заключается в том, что текущее значение слишком низкое.Таким образом, вызов nextval (или того, что использует JPA) вернет идентификатор, который уже существует.

У меня есть следующее в JPA-Entity:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "enterprise_id_seq")
@Column(name = "id", nullable = false)
private Integer id;

И моя последовательность выглядит следующим образом:

test_db=# \d enterprise_id_seq 
      Sequence "public.enterprise_id_seq"
    Column     |  Type   |        Value        
---------------+---------+---------------------
 sequence_name | name    | enterprise_id_seq
 last_value    | bigint  | 19659
 start_value   | bigint  | 1
 increment_by  | bigint  | 1
 max_value     | bigint  | 9223372036854775807
 min_value     | bigint  | 1
 cache_value   | bigint  | 1
 log_cnt       | bigint  | 32
 is_cycled     | boolean | f
 is_called     | boolean | t

Я получаю следующие ошибки:

[...]

Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-
r6600): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "enterprise_pkey"
    Detail: Key (id)=(19611) already exists.
Error Code: 0
Call: INSERT INTO en...

[...]

Как видите, он пытается вставить сущность с идентификатором 19611, но последним значением в последовательностиэто 19659.Что явно не так.

Я также попытался перезапустить сервер приложений за всем этим, чтобы закрыть все открытые соединения и сеансы.Не повезло ... Еще одна вещь, которую я заметил: поле определяется как Integer.Должно ли это быть Long?Это потребовало бы некоторых изменений в коде, и у меня еще не было времени, чтобы это исправить.

Поскольку у меня всего 50 записей, я мог бы просто попытаться выполнить вставку 50 раз, но я быа точнее точно знаешь, что пошло не так ...

Что мне здесь не хватает?

Обновление: После еще нескольких копаний я наткнулся на allocSize который имеет значение по умолчанию 50. Интересно, что это довольно близко к разнице идентификаторов, которые я вижу.Это может быть не на 100% одинаково из-за некоторых испытаний и обморожений.Может ли это быть связано?Я, честно говоря, не понял идею этого параметра ...

Ответы [ 2 ]

5 голосов
/ 16 февраля 2011

Конечно, для Hibernate по умолчанию при использовании GenerationType.SEQUENCE используется стратегия hi / lo, до allocationSize идентификаторов перед значением, возвращаемым из базы данных.Установите для allocSize значение 1, и оно должно быть DTRT.

Предыдущий ответ на очень похожую проблему: Hibernate, генерирующий два разных идентификатора последовательности для вставки PostgreSQL

2 голосов
/ 17 февраля 2011

Да, это потому, что ваш размер_раздела равен 50 (по умолчанию).Мы, EclipseLink, выполняем next_value, предполагая, что приращение равнялось 50, то же самое относится и к предыдущим 50 идентификаторам.

allocSize должен соответствовать вашему приращению последовательности.Я бы порекомендовал вам обновить шаг приращения до 50, это позволит предварительно распределить последовательность, что значительно улучшит вашу производительность.

Если вы хотите придерживаться значения 1, тогда измените ваш размер размещения в аннотации на 1.

Я бы порекомендовал long для идентификатора, но int будет безопасен до 4 294 967 296, поэтому зависит, если вы думаете, что у вас будет более 4 миллиардов строк в жизни вашего приложения.

...