Избегайте дублирования первичных ключей после инициализации базы данных в spring-boot 2

Стратегия @GeneratedValue по умолчанию, используемая для работы в веб-приложении spring boot 1.5, без дублирующих конфликтов идентификаторов любого типа.

... используя простую сущность, такую ​​как эта

// in my/package/Car.java
// ...
@Entity
public class Car {
    private long id;
    private String company;
    private String model;

    @Id
    @GeneratedValue
    public long getId() {
        return id;
    }

    // ... more getters and setters
}

... и инициализация БД при запуске с помощью

# in src/main/resources/import.sql
insert into car values (1, 'Tesla', 'Roadster');

... а затем вставить другую машину с

Car c = new Car();
c.setCompany("Ford");
c.setModel("Pinto");
entityManager.persist(c);
entityManager.flush();

// expect no issue inserting, and a valid ID
log.info("Assigned ID is " + c.getId());

... раньше приводил к новому Car с идентификатором 2. Меня не очень волнует сгенерированный ID, пока нет конфликта. Однако этот же код теперь выдает следующее исключение:

org.hsqldb.HsqlException: integrity constraint violation: unique constraint or index violation; SYS_PK_10095 table: CAR (БД - это HSQL, и мне бы не хотелось ее заменять)

... потому что генерация последовательности по умолчанию в спящем режиме 5.2 теперь не учитывает существующие вставки.

Каковы мои возможные обходные пути, чтобы по-прежнему разрешать инициализацию базы данных через import.sql? я знаю что могу

  • используйте очень большие идентификаторы во время инициализации (но это просто пинок в сторону, а не реальное решение: в конечном итоге последовательность догонит и сломает вещи)
  • написать свой собственный генератор последовательности (но должен быть гораздо более простой способ инициализации БД!)
  • использовать старую генерацию последовательности (но опять же, почему они изменили ее, если в этом не было никаких преимуществ? Разработчики спящего режима наверняка имели в виду какой-то лучший способ инициализации!).
  • как-то указать начальное значение для новых идентификаторов (как мне сделать это безотказным способом? есть ли свойство, которое может войти в мой application.properties, чтобы сохранить это централизованно?)

Я хочу использовать это в контексте веб-приложения с весенней загрузкой и сделать его максимально простым и близким к лучшим практикам. Предложения?

Привет, я не уверен, правильно ли я понял ваш вопрос, но у меня была аналогичная проблема, и ошибка заключалась в том, что я вставлял элемент с помощью инструкции SQl. В этом заявлении для идентификатора установлено фиксированное значение, но, насколько мне известно, Spring управляет вашим GeneratedValue, и он не смог распознать установленный вручную идентификатор. Поэтому, когда вы вставляете элемент с использованием контекста Spring, последовательность GeneratedValue начинается с 1 и выдает ошибку, поскольку идентификатор уже существует.

user8914226 01.03.2019 12:51

@MatthiasLauber да, в этом проблема. Однако мне нужно иметь возможность вставлять операторы SQL, потому что в противном случае я не могу включать отношения (которые требуют, чтобы идентификатор соответствовал значению внешнего ключа). Как бы вы инициализировали БД с идентификаторами в противном случае? Альтернативный способ инициализации идентификатора был бы допустимым ответом.

tucuxi 01.03.2019 13:05

Вы пользуетесь Flyway? Там вы пишете схему SQL для своих таблиц. Вы можете определить последовательность и указать своим таблицам использовать следующее значение последовательности для генерации идентификатора и вставить последовательность в свои объекты Spring.

user8914226 01.03.2019 13:23

@MatthiasLauber Я не использую Flyway и хотел бы свести к минимуму зависимости. Тем не менее, это кажется рабочим решением: спасибо.

tucuxi 01.03.2019 14:55
0
4
2 072
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Начиная с версии 5 вместо IDENTITY для генерации идентификатора используется SEQUENCE. Миграция с Hibernate 4 на 5

Что случилось?

Вы вставили запись с ID 1 с помощью скрипта. Последовательность остается равной 1. Она хочет вставить 1, что вызывает уникальное нарушение PK.

Решение

Не используйте автоматический тип генерации. Используйте ИДЕНТИЧНОСТЬ. Затем при вставке записей скриптом IDENTITY будет автоматически увеличиваться. Также вам не нужно вставлять значение идентификатора:

 DECLARE temp_id INTEGER;
 INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP);
 SET temp_id = IDENTITY();
 INSERT INTO ADDRESSES VALUES (DEFAULT, temp_id, address);

Да, я уже проверил, что этот дублирующийся первичный ключ был проблемой (если идентификаторы начинаются, скажем, с 10 в insert.sql, проблем не обнаружено; это пункт № 1 моего списка обходных путей, который, очевидно, не сработает, учитывая достаточно времени). Использование IDENTITY кажется возможным, однако я не понимаю, почему мне не нужно вставлять идентификаторы: как еще я могу добавить отношения через import.sql? Есть ли другой способ инициализировать БД без идентификаторов, но с отношениями «один ко многим» и «многие ко многим»?

tucuxi 01.03.2019 14:58

последнее вставленное значение в столбец идентификации для соединения доступно с помощью функции IDENTITY()

Peter Šály 01.03.2019 15:05

Ссылка не работает; Я нашел это: hsqldb.org/doc/guide/builtinfunctions-chapt.html; однако я до сих пор не знаю, как использовать это в файле import.sql, чтобы, скажем, указать, что Car 1 управляется Person 2, предполагая отношение Person_Car, которое принимает car_id и person_id

tucuxi 01.03.2019 15:14

Другие вопросы по теме