Почему при миграции БД через flyway sequence не обновляется?
Приведу простой пример: Используемая БД - PostgresSQL. Используемый стек: Spring Boot + Hibernate + FlyWay
@Data
@Entity
@Table(name = "books")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long bookId;
//....
}
Вот, генерируемая hibernate'ом таблица
create table if not exists books
(
book_id bigserial not null
constraint books_pkey
primary key,
language_id bigint
constraint fksp7ty25kndaxyfrgkrloo1dd8
references author
);
Пример sql запросов для импорта:
INSERT INTO "public"."books" ("book_id", "author_id") VALUES (1, 2);
После миграции данных через flyway sequence не обновился. Т.е. у меня должно быть более 200 объектов, а sequence индекс показывает 1.
SELECT last_value FROM books_book_id_seq;
last_value 0
При этом например, для другой сущности, которая была инициализирована java скриптом индекс отображается корректно. Проблема чем-то похожа на эту: Ссылка
И зачем hibernate генерирует sequences, если GenerationType.IDENTITY ? В чем заключается моя ошибка?
Обновлено
Попробовал другое - сменил GenerationType.SEQUENCE hibernate создала hibernate_sequence Но также изменила DDL таблицы на (bigserial -> bigint):
create table if not exists books
(
book_id bigint not null
constraint books_pkey
primary key,
language_id bigint
constraint fksp7ty25kndaxyfrgkrloo1dd8
references author
);
В любом случае при миграции через flyway значение не инкрементируется. Максимум могу вручную подправить это значение.
Ну а так как значение в sequence некорректное, то вот Exception - DataIntegrityViolationException
Заранее благодарю за ответы!
Ответы (1 шт):
Почему при миграции БД через flyway sequence не обновляется?
Тут есть недопонимание, как работает serial/bigserial тип в postgres.
Это тип, который, в случае если вы не задали значение при создании записи, сгенерирует значение из sequence. Если же значение для поля было явно задано (как в случае миграции, которую вы создали), то оно вставляется, как есть, а sequence при этом не модифицируется.
Исправить это можно одним из трех способов. При создании записей в миграции можно:
- не указывать
book_id, чтобы сработал механизм автоматической генерации - либо вручную использовать
sequenceпривязанный к полюbook_id - либо вручную обновить
sequenceпосле вставки всех записей
Рассмотрим эти варианты
Не указывать book_id
INSERT INTO "public"."books" ("author_id") VALUES (2);
Если нужно сослаться, на только что сгенерированный идентификатор это можно сделать так (на примере с автором):
INSERT INTO "public"."author" ("name") VALUES ('somebody');
INSERT INTO "public"."books" ("author_id")
SELECT currval(pg_get_serial_sequence('books', 'author_id'));
вручную использовать sequence
Честно говоря, сильного смысла вариант не имеет, но для полноты картины включаю его тоже. Выглядеть запрос будет так:
INSERT INTO "public"."books" ("book_id", "author_id")
SELECT nextval(pg_get_serial_sequence('books', 'book_id')), 2;
вручную обновить sequence после вставки всех записей
SELECT pg_catalog.setval(
pg_get_serial_sequence('books', 'book_id'),
(SELECT MAX(book_id) FROM "public"."books") + 1);
зачем hibernate генерирует sequences, если GenerationType.IDENTITY
Тут все не совсем так. sequence создает не hibernate, а postgres.
IDENTITY означает, что поле автоматически сгенерируется БД, если оно не задано. Т.е. hibernate при создании новой сущности в БД просто не передает это поле.
И это ответственность БД сгенерировать значение автоматически.
В postgres типы с автогенерацией значения - serial/bigserial. То что внутри используется sequence - это деталь реализации автоматической генерации в postgres (ведь он создается автоматически и пользователю в большинстве случаев не нужно о нем заботится - поле автоматически генерируется и это то, что нужно). Именно поэтому, этот sequence нигде не фигурирует в маппинге hibernate.
А для GenerationType.SEQUENCE нужно явно указать в маппинге, какой sequence используется. При создании новой записи hibernate выполнит запрос к БД, чтобы сгенерировать значение для поля, используя указаный в конфигурации поля sequence. И потом использует это значение непосредственно при выполнении INSERT.
Когда вы сменили тип генератора на GenerationType.SEQUENCE, то в этом случае hibernate создает другой тип поля в БД - bigint, т.е. тип без автогенерации, потому, что этим вы явно указали использовать sequence.