Как increment_by последовательности PostgreSQL работает с размером распределения Hibernate?

#java #postgresql #hibernate #orm #hibernate-mapping

#java #postgresql #режим гибернации #orm #режим гибернации-сопоставление

Вопрос:

Я настроил тестовое приложение с использованием Hibernate, PostgreSQL и Spring, и я продолжаю сталкиваться с этой необъяснимой ошибкой. Я надеюсь, что кто-нибудь здесь может пролить некоторый свет на проблему.

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

 @Entity
@Table(name = "something")
public class Something {

    @Id
    @SequenceGenerator(name = "somethingGen", sequenceName = "something_id_seq", allocationSize = 30)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "somethingGen")
    @Column(name = "id", unique = true, nullable = false)
    private long id;

    public Something() {
    }

    public long getId() {
        return id;
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", Something.class.getSimpleName()   "[", "]")
                .add("id="   id)
                .toString();
    }
}

  

В целях тестирования я создал хранилище данных Spring, вставил пять записей и извлек их обратно:

 System.out.println("starting...");

for (int i = 0; i < 5; i  ) {
    repository.save(new Something());
}

repository.findAll().forEach(System.out::println);
  

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

 Something[id=1]
Something[id=2]
Something[id=3]
Something[id=4]
Something[id=5]
  

Однако во второй раз я сталкиваюсь с этим исключением: org.hibernate.MappingException: The increment size of the [something_id_seq] sequence is set to [30] in the entity mapping while the associated database sequence increment size is [1].

Итак, я проверил последовательность в Postgres DB:

 uat=# d something_id_seq
      Sequence "public.something_id_seq"
    Column     |  Type   |        Value        
--------------- --------- ---------------------
 sequence_name | name    | something_id_seq
 last_value    | bigint  | 1
 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
  

Это первая проблема. Похоже, что Hibernate создает последовательности с неправильным значением приращения. Итак, я изменил значение increment_by: alter sequence something_id_seq increment 30 и снова запустил свой код, и в итоге получился такой результат:

 Something[id=1]
Something[id=2]
Something[id=3]
Something[id=4]
Something[id=5]
Something[id=901]
Something[id=902]
Something[id=903]
Something[id=904]
Something[id=905]
  

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

 --------------- --------- ---------------------
 sequence_name | name    | something_id_seq
 last_value    | bigint  | 31
 start_value   | bigint  | 1
 increment_by  | bigint  | 30
  

Итак, мне кажется, что новый идентификатор вычисляется через increment_by * allocation_size last_value , но я ожидал, что новый идентификатор будет равен 30 ( allocation_size * last_value ), поскольку я подозревал, что это increment_by и allocationSize будет одно и то же из-за исключения MappingException .

Это оставляет меня с двумя вопросами:

  1. Я ожидал, что идентификаторы будут около 0, 30, 60, 90… и вместо этого я получил идентификаторы около 1 и 900. Javadoc SequenceGenerator утверждает, что размер распределения равен the amount to increment by when allocating sequence numbers from the sequence . Означает ли это, что increment_by и allocationSize это не одно и то же? Как они связаны друг с другом (например, почему я вижу MappingException ) и как мне добиться ожидаемого поведения?

  2. Исключение MappingException указывает мне, что Hibernate создает последовательности с неправильным значением приращения. Как мне заставить Hibernate создать правильный?

Я использую Hibernate 5.4.2.Final и PostgreSQL 9.6.12 и эти настройки:

 HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL);
hibernateJpaVendorAdapter.setDatabasePlatform(PostgreSQL9Dialect.class.getName());
hibernateJpaVendorAdapter.setGenerateDdl(true);
  

Я подозреваю, что проблема может быть где-то с генератором HiLo, но, хоть убейте, я не могу в этом разобраться:

 hibernate.id.new_generator_mappings=true
hibernate.id.optimizer.pooled.preferred=hilo
hibernate.schema_update.unique_constraint_strategy=RECREATE_QUIETLY
  

Ответ №1:

increment_by и allocationSize должны быть того же размера. Однако, если у вас нет распределенной системы с большим количеством вставок, вам следует просто использовать значение по умолчанию, increment_by равное 1, и использовать @GeneratedValue(strategy = GenerationType.IDENTITY) .

Вероятно, в конечном итоге вы переходите к 900 из-за того, что различные потоки резервируют идентификатор, но затем не выполняют вставку. У меня была эта проблема. Теперь я вставляю с IDENTITY .

Что касается исключения MappingException, something_id_seq последовательность создается автоматически postgres, потому что диалект PostgreSQL использует serial or bigserial при создании таблицы. Смотрите PostgreSQL81IdentityColumnSupport. Возможно, следует считать ошибкой то, что Hibernate не изменяет последовательность на allocationSize , и вы можете сообщить об этом.

Комментарии:

1. Я не использую никаких потоков. Приведенный выше пример кода на самом деле представляет собой весь код приложения. Я не думаю, что последовательность генерируется PostgreSQL, потому что оператор create регистрируется, и если я не использую HiLo, то приращение правильное.