Рубрики
Без рубрики

Как перенести оптимизатор идентификаторов hilo Hibernate в объединенную стратегию

Узнайте, как перейти от устаревшего оптимизатора идентификаторов сущностей на основе последовательности hilo к стратегии объединения при использовании Hibernate.

Автор оригинала: Vlad Mihalcea.

Вступление

В этой статье я покажу вам, как перейти от устаревшего оптимизатора идентификаторов на основе последовательности hilo к стратегии гибернации в пуле.

Я решил написать эту статью после обсуждения с Гердом Ашманном в Твиттере о решении проблемы HHH-13783 гибернации.

Как перейти от устаревшего оптимизатора идентификаторов на основе последовательностей hilo к стратегии гибернации в пуле. https://t.co/axf70HwMU4

Генератор идентификаторов последовательности по умолчанию

Давайте предположим, что у нас есть следующее Post сущность, которая использует генератор последовательностей post_sequence базы данных для автоматического создания идентификаторов сущностей при сохранении.

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue(
        strategy = GenerationType.SEQUENCE,
        generator = "post_sequence"
    )
    @SequenceGenerator(
        name = "post_sequence",
        sequenceName = "post_sequence",
        allocationSize = 1
    )
    private Long id;

    private String title;

    //Getters and setters omitted for brevity
}

Теперь при вставке 5 Post сущностей:

for (int i = 0; i < 4; i++) {
    Post post = new Post();
    post.setTitle(
        String.format(
            "High-Performance Java Persistence, Part %d",
            i + 1
        )
    );

    entityManager.persist(post);
}

Hibernate генерирует следующие инструкции SQL, предполагая, что мы используем PostgreSQL:

CALL NEXT VALUE FOR post_sequence;
CALL NEXT VALUE FOR post_sequence;
CALL NEXT VALUE FOR post_sequence;
CALL NEXT VALUE FOR post_sequence;

-- Flushing the Persistence Context

INSERT INTO post (title, id) 
VALUES ('High-Performance Java Persistence, Part 1', 1)

INSERT INTO post (title, id) 
VALUES ('High-Performance Java Persistence, Part 2', 2)

INSERT INTO post (title, id) 
VALUES ('High-Performance Java Persistence, Part 3', 3)

INSERT INTO post (title, id) 
VALUES ('High-Performance Java Persistence, Part 4', 4)

Когда метод persist вызывается для каждой сущности Post , Hibernate вызывает последовательность post_sequence базы данных для создания значения идентификатора сущности, необходимого для создания ключа, под которым сущность будет связана с текущим контекстом сохранения (например, кэш первого уровня).

Когда flush вызывается Hibernate перед совершением транзакции базы данных, в Объекты Post вставляются в базу данных с использованием ранее выделенных значений идентификаторов.

Теперь, если мы знаем, что обычно мы сохраняем более одного Post сущность, тогда мы могли бы уменьшить количество вызовов последовательности баз данных, создав несколько значений идентификаторов для одного значения последовательности базы данных. И это именно тот случай использования оптимизаторов идентификаторов на основе последовательностей.

Оптимизатор Hilo

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

С помощью одного вызова последовательности базы данных мы можем генерировать несколько значений идентификаторов в приложении. Значение последовательности базы данных представляет значение hi в то время как lo значение увеличивается с 0 к размер распределения значение для каждого конкретного привет значение.

Итак, давайте изменим Сообщение идентификатор сущности для использования генератора hilo:

@Id
@GeneratedValue(
    strategy = GenerationType.SEQUENCE, 
    generator = "post_sequence"
)
@GenericGenerator(
    name = "post_sequence",
    strategy = "sequence",
    parameters = {
        @Parameter(name = "sequence_name",  value = "post_sequence"),
        @Parameter(name = "initial_value",  value = "1"),
        @Parameter(name = "increment_size",  value = "3"),
        @Parameter(name = "optimizer", value = "hilo")
    }
)
private Long id;

При сохранении тех же 5 Post сущностей, которые мы создали ранее, Hibernate выполнит следующую инструкцию SQL для оптимизатора hilo:

CALL NEXT VALUE FOR post_sequence;
CALL NEXT VALUE FOR post_sequence;

-- Flushing the Persistence Context

INSERT INTO post (title, id) 
VALUES ('High-Performance Java Persistence, Part 1', 1)

INSERT INTO post (title, id) 
VALUES ('High-Performance Java Persistence, Part 2', 2)

INSERT INTO post (title, id) 
VALUES ('High-Performance Java Persistence, Part 3', 3)

INSERT INTO post (title, id) 
VALUES ('High-Performance Java Persistence, Part 4', 4)

Таким образом, были выполнены только вызовы последовательности 2 базы данных, так как первые 3 Post сущности использовали первое значение последовательности базы данных 1 для создания идентификаторов сущностей со значениями 1 , 2 , и 3 . Для 4-й сущности Post Hibernate требовался вызов новой последовательности базы данных, а для hi значения 2 , Hibernate может генерировать значения идентификаторов сущностей 4 и 5 .

Однако проблема с hilo заключается в том, что значение последовательности базы данных не включено в границы сгенерированных идентификаторов сущностей. Таким образом, сторонний клиент, который может не знать о стратегии hilo, которую мы используем, не будет знать, какое значение использовать для следующего значения идентификатора, поскольку значения последовательности базы данных должны быть умножены на Размер распределения . Именно по этой причине Hibernate представил оптимизатор с объединением и объединением-lo .

Объединенный оптимизатор

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

По этой причине для использования объединенного оптимизатора требуется только указать размер распределения через @SequenceGenerator аннотацию JPA:

@Id
@GeneratedValue(
    strategy = GenerationType.SEQUENCE, 
    generator = "post_sequence"
)
@SequenceGenerator(
    name = "post_sequence",
    sequenceName = "post_sequence",
    allocationSize = 3
)
private Long id;

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

Итак, при сохранении того же 5 Post сущности, Hibernate выполняет те же инструкции SQL, которые также сгенерировал оптимизатор hilo. Однако на этот раз в последовательности pooled_sequence базы данных используется УВЕЛИЧЬТЕ шаг, равный размеру выделения атрибута @SequenceGenerator аннотации:

CREATE SEQUENCE post_sequence START 1 INCREMENT 3

Переход от Hibernate hilo к объединенному оптимизатору

Таким образом, мы не можем просто изменить аннотации JPA при переходе с устаревшего hilo на более совместимый оптимизатор пула. Нам также необходимо изменить последовательность базовых баз данных.

Если мы попытаемся это сделать, Hibernate выдаст следующее Исключение сопоставления :

javax.persistence.PersistenceException: [PersistenceUnit: ] 
    Unable to build Hibernate SessionFactory
        
Caused by: org.hibernate.MappingException: 
    Could not instantiate id generator 
    [entity-name=com.vladmihalcea.book.hpjp.hibernate.identifier.Post]

Caused by: org.hibernate.MappingException: 
    The increment size of the [post_sequence] sequence is set to [3] 
    in the entity mapping while the associated database sequence 
    increment size is [1].

К счастью, это можно сделать очень легко, используя всего 2 инструкции SQL, которые необходимо запустить перед загрузкой в режим гибернации. Обычно это делается с помощью сценариев миграции, которые запускаются таким инструментом, как Flyway:

SELECT setval('post_sequence', (SELECT MAX(id) FROM post) + 1)

ALTER SEQUENCE post_sequence INCREMENT BY 3

Обратите внимание, что эти 2 Инструкции SQL, которые изменяют последовательность базы данных в соответствии с требованиями pooledoptimizer, были написаны для PostgreSQL. Для других систем реляционных баз данных вам необходимо изменить эти инструкции, чтобы они соответствовали синтаксису DDL для конкретной базы данных СУБД, используемой вашим приложением.

Вот и все! Теперь вы можете использовать объединенный оптимизатор вместо оптимизатора hilo, и все должно работать как надо.

Вывод

Хотя оптимизатор hilo может оптимизировать количество вызовов последовательности баз данных, вы должны отдавать предпочтение использованию оптимизаторов с объединением или с объединением в пул, поскольку они совместимы со сторонними системами или клиентами, которые могут не знать о стратегии hilo, используемой логикой приложения.

Таким образом, при переходе из hilo в пул, помимо обновления сопоставления идентификаторов сущностей JPA, вам необходимо изменить последовательность базы данных, чтобы она начиналась со значения, превышающего максимальное значение первичного ключа таблицы, а также изменить шаг приращения последовательности в соответствии с атрибутом allocationSize .