Команда Spring АйО перевела статью про новые функции и возможности, добавленные в Hibernate ORM версии 7.0.: новая лицензия Apache 2.0, переход на Jakarta Persistence 3.2 и Java 17, замена HCANN на Hibernate Models, а также множество улучшений для работы с JSON, XML, enum, soft-delete и запросами.

Добавлено экспериментальное API для batch-операций, Set-returning functions и удобный доступ к кэшу первого уровня.

Если вы переходите с версии 6.6, обязательно ознакомьтесь с Руководством по миграции, где обсуждаются наиболее значимые изменения.

Лицензия Apache

Начиная с версии 7.0, Hibernate ORM будет распространяться по лицензии Apache License 2.0. Подробнее об этом можно узнать по ссылке: https://hibernate.atlassian.net/browse/HHH-19145.

В рамках этого перехода команда Hibernate связалась с авторами нетривиальных контрибьюшенов в проект, чтобы получить разрешение на переиздание их кода под новой лицензией. Большинство откликнулись положительно, однако с некоторыми авторами не удалось установить контакт, а один из них выразил несогласие. Это потребовало от команды ряда действий:

Java 17

Java 17 стала новой базовой версией Java для Hibernate ORM.

Jakarta Persistence 3.2

Hibernate ORM 7.0 переходит на Jakarta Persistence 3.2, что сопровождается достаточно серьёзными изменениями. Подробный разбор этих изменений можно найти в соответствующей записи блога.

Hibernate Models

Многие годы Hibernate использовал библиотеку Hibernate Commons Annotations (HCANN) для решения различных низкоуровневых задач — таких как анализ структуры доменной модели приложения, чтение аннотаций и интеграция XML-маппингов.

С выходом версии 7.0 вместо HCANN используется новый проект — Hibernate Models, разработанный как более современная и эффективная альтернатива.

@SoftDelete с TIMESTAMP

Теперь механизм мягкого удаления (soft-delete) поддерживает стратегию отслеживания времени удаления — с помощью метки времени (timestamp), фиксирующей момент, когда запись была помечена как удалённая. Это дополняет ранее доступные стратегии, основанные на логическом признаке (true/false).

Подробности приведены в Руководстве пользователя.

@EmbeddedColumnNaming

Одной из давно запрашиваемых функций в Hibernate и Jakarta Persistence была возможность задавать префикс для имён колонок, связанных с embedded значениями.

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

@Embeddable
class Address {
    String street;
    String city;
	...
}

@Entity
class Person {
    ...
    @Embedded
    @EmbeddedColumnNaming("home_%")
    Address homeAddress;

    @Embedded
    @EmbeddedColumnNaming("work_%")
    Address workAddress;
}

Фича находится в статусе экспериментальной (incubating).

Подробности приведены в Руководстве пользователя.

@NamedEntityGraph

Добавлена новая аннотация — @org.hibernate.annotations.NamedEntityGraph, которая позволяет задавать именованную граф-схему сущности, используя возможность Hibernate интерпретировать её строковое представление.

@Entity
@NamedEntityGraph( graph="title, isbn, author(name, phoneNumber)" )
class Book {
	// ...
}

Подробнее о синтаксисе см. в классе org.hibernate.graph.GraphParser, а также в Руководстве пользователя.

findMultiple() и getMultiple()

Новые методы Session.findMultiple() и StatelessSession.getMultiple() обеспечивают удобный способ загрузки сразу нескольких сущностей по их идентификаторам.

В сочетании с параметром BatchSize они позволяют разбивать вызовы к базе данных на батчи (batches), что повышает эффективность работы с JDBC.

FindOption, LockOption, RefreshOption

В Jakarta Persistence появилась более типобезопасная альтернатива использованию hints — так называемые «объекты опций» (option objects), которые указывают настройки операций. Это не только повышает безопасность типов, но и делает код более лаконичным!

Эти объекты опций бывают трёх типов, в зависимости от того, какая операция выполняется:

  • FindOption — используется для настройки операций поиска, таких как:

    • Session.find

    • Session.findMultiple

    • и другие

  • LockOption — применяется при вызове Session.lock

  • RefreshOption — используется для Session.refresh

Каждый из этих типов представлен в виде интерфейса. Jakarta Persistence предоставляет несколько встроенных опций:

  • CacheRetrieveMode — теперь реализует интерфейс FindOption

  • CacheStoreMode — теперь реализует FindOption и RefreshOption

  • LockModeType — теперь реализует FindOption и RefreshOption

  • Timeout (новый) — реализует все три интерфейса: FindOption, RefreshOption, LockOption. Дополнительно см. org.hibernate.Timeouts для специальных значений

  • PessimisticLockScope — теперь реализует все три интерфейса

Hibernate также добавляет собственные опции:

  • ReadOnlyMode (новая) — FindOption, указывает, следует ли рассматривать загружаемые сущности и коллекции как только для чтения

  • EnabledFetchProfile (новая) — FindOption, указывает имя fetch-профиля, который должен быть активирован во время поиска

  • LockMode — теперь FindOption и RefreshOption, расширенная версия LockModeType из JPA

  • CacheMode — теперь FindOption

  • BatchSize (новая) — теперь FindOption

Все методы, принимающие опции (а ранее принимавшие hints), теперь поддерживают передачу переменного числа аргументов (varargs). Например:

Book loaded = session.find(
    Book.class,
    1,
    LockMode.PESSIMISTIC_WRITE,
    Timeouts.NO_WAIT,
    new EnabledFetchProfile("with-authors")
);

QuerySpecification, Restriction и Range

В Hibernate 7.0 появилось новое API для поэтапного построения запросов с поддержкой выборок и условий.

Пример выборочного запроса (SelectionQuery):

SelectionQuery<Book> qry = SelectionSpecification.create(
    Book.class,
    "from Book"
).restrict(
    Restriction.restrict(
        Book_.suggestedCost,
        Range.closed(10.00, 19.99)
    )
).sort(
    Order.asc(Book_.suggestedCost)
).createQuery(session);

Пример мутационного запроса (MutationQuery):

MutationQuery<Book> qry = MutationSpecification.create(
    Book.class,
    "delete Book"
).restrict(
    Restriction.restrict(
        Book_.suggestedCost,
        Range.closed(10.00, 19.99)
    )
).createQuery(session);

Фича находится в статусе экспериментальной (incubating).

Подробности приведены в Руководстве пользователя.

Прямой доступ к кэшу первого уровня

В версии 7.0 добавлен новый метод Session.getManagedEntities(), который позволяет приложению перебрать все сущности, находящиеся в кэше первого уровня, либо ограничиться сущностями определённого типа.

Фича находится в статусе экспериментальной (incubating).

Использование метамодели при привязке параметров запроса

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

Пример использования:

session.createSelectionQuery("from Thing where uniqueKey = ?1")
        .setParameter(1, key, Thing_.uniqueKey.getType())
        .getSingleResult();

Преобразованные перечисления (Enums) и ограничения CHECK

Ранее Hibernate уже поддерживал генерацию ограничений CHECK для перечислений (enum), отображаемых с помощью аннотации @Enumerated при генерации схемы базы данных.

В версии 7.0 эта возможность расширена: теперь CHECK-ограничения автоматически создаются и для перечислений, преобразуемых с помощью AttributeConverter. Hibernate выполняет преобразование всех значений enum при запуске, чтобы определить допустимые значения для включения в ограничение.

Функции для работы с JSON и XML

В Hibernate 7.0 в язык HQL и API Criteria добавлена поддержка большинства функций для работы с JSON и XML, предусмотренных SQL-стандартом.

Реализации этих функций сохраняют семантику SQL и вызывают ошибку, если эмуляция на конкретной базе данных невозможна.

Добавлены следующие категории функций:

  • Функции построения: json_array(), json_object(), xmlelement(), xmlforest()

  • Функции запроса: json_value(), json_query(), xmlquery()

  • Агрегатные функции: json_agg(), json_object_agg(), xmlagg()

  • Функции изменения данных: json_set(), json_mergepatch()

  • И многие другие

Функции, возвращающие набор строк (Set-returning Functions)

В Hibernate 7.0 добавлена поддержка функций, возвращающих набор строк — нового типа функций, которые могут возвращать строки и применяются исключительно для использования в предложении from.

В разных диалектах SQL такие функции известны как table-valued functions или table functions.

Пользовательские функции этого типа можно регистрировать с помощью интерфейса FunctionContributor. Hibernate также предоставляет встроенную поддержку (или эмуляцию) для наиболее распространённых функций:

  • unnest() — превращает массив в набор строк

  • generate_series() — создаёт последовательность значений в виде строк

  • json_table() — преобразует JSON-документ в строки

  • xmltable() — преобразует XML-документ в строки

@AnyDiscriminatorImplicitValues

Новая аннотация @AnyDiscriminatorImplicitValues предоставляет два важных улучшения в работе с дискриминаторами при использовании ассоциаций @Any и @ManyToAny.

  1. Управление значением дискриминатора
    Ранее Hibernate всегда сохранял полное имя связанной сущности как значение дискриминатора в базе данных при использовании неявной (implicit) стратегии. Теперь с помощью этой аннотации можно контролировать, какое именно значение будет использоваться.

  2. Смешивание явных и неявных стратегий
    Аннотация также позволяет комбинировать явно заданные дискриминаторы с неявно определяемыми, обеспечивая большую гибкость в маппинге.

Фича находится в статусе экспериментальной (incubating).

Подробности доступны в Руководстве пользователя.

StatelessSession и пакетные операции

StatelessSession теперь поддерживает явные пакетные операции с помощью методов insertMultiple(), updateMultiple() и deleteMultiple().

Фича считается экспериментальной.

StatelessSession и кэш второго уровня

Ранее StatelessSession никогда не взаимодействовал с кэшем второго уровня. Это соответствовало его изначальной роли в пакетной обработке данных. Однако с появлением Jakarta Data и репозиториев данных Hibernate обязанности StatelessSession расширились, и прежнее поведение стало неактуальным. Теперь StatelessSession по умолчанию использует кэш второго уровня.

Дополнительную информацию можно найти в руководстве по миграции.

StatelessSession и пакетирование JDBC

Автоматический batching JDBC приводит к отложенному выполнению операций, что нарушает синхронную природу работы через StatelessSession. В Hibernate 7 конфигурационное свойство hibernate.jdbc.batch_size больше не влияет на StatelessSession. Автоматический batching всё ещё можно включить вручную, вызвав метод setJdbcBatchSize(). Однако предпочтительным способом считается явное выполнение пакетных операций с помощью методов insertMultiple(), updateMultiple() или deleteMultiple().

Улучшения в Transaction

Через интефейс Transaction можно получить доступ к SPI типу TransactionStatus (метод getStatus) и типу Synchronization, определённому в JTA, через registerSynchronization(), что нарушает принцип разделения слоёв — пусть и не критичным образом. В интерфейс Transaction были добавлены новые операции, позволяющие проверять статус транзакции или регистрировать обратные вызовы без использования подобных нарушающих слоение методов.

Фича считается экспериментальной.

@CollectionIdJavaClass

Аннотация @CollectionIdJavaClass является альтернативой @CollectionIdJavaType для более простых случаев отображения коллекций с идентификаторами (id-bag). Например:

@Bag
@CollectionId(generator="increment")
@CollectionIdJavaClass(Integer.class)
Collection<Person> authors;

Заполнение данных

Установка свойства jakarta.persistence.schema-generation.database.action=populate или вызов метода SchemaManager.populate() позволяет заполнить существующую схему начальными данными из файла /import.sql или других SQL-скриптов, указанных через свойство jakarta.persistence.sql-load-script-source.

XML-мэппинги

Устаревшая схема мэппинга hbm.xml в Hibernate была давно признана устаревшей и заменена на новую схему mapping.xml, которая теперь считается стабильной и рекомендуется к использованию. Поддержка hbm.xml будет окончательно удалена в версии 8.0.

В новой версии предлагается инструмент для преобразования файлов hbm.xml в формат mapping.xml, доступный как на этапе сборки (плагин для Gradle), так и во время выполнения приложения — с помощью параметра hibernate.transform_hbm_xml.enabled=true.


Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.

Комментарии (0)