Автор оригинала: Vlad Mihalcea.
Вступление
В этой статье я покажу вам, как вы можете использовать записи Java с JPA и гибернацией.
Добавленные с версии 14 в качестве функции предварительного просмотра, записи Java позволяют нам создавать компактные DTO (Объекты передачи данных) или объекты значений.
Модель предметной области
Давайте предположим, что в нашем приложении есть следующий класс Post
entity:
Обратите внимание , что сущность Post
использует API в стиле , который позволяет нам создавать экземпляры сущностей, подобные этому:
entityManager.persist( new Post() .setId(1L) .setTitle("High-Performance Java Persistence") .setCreatedBy("Vlad Mihalcea") .setCreatedOn( LocalDateTime.of(2016, 11, 2, 12, 0, 0) ) .setUpdatedBy("Vlad Mihalcea") .setUpdatedOn( LocalDateTime.now() ) ); entityManager.persist( new Post() .setId(2L) .setTitle("Hypersistence Optimizer") .setCreatedBy("Vlad Mihalcea") .setCreatedOn( LocalDateTime.of(2020, 3, 19, 12, 0, 0) ) .setUpdatedBy("Vlad Mihalcea") .setUpdatedOn( LocalDateTime.now() ) );
Могут ли записи Java использоваться в качестве объектов JPA или гибернации?
Один очень распространенный вопрос заключается в том, собираются ли записи Java упростить способ создания объектов JPA или гибернации. И ответ-нет. Они этого не сделают.
В соответствии со спецификацией JPA организация должна следовать этим требованиям:
- класс сущности должен быть не
окончательным
, - класс сущностей должен иметь конструктор без аргументов, который является либо
общедоступным
, либозащищенным
, - атрибуты сущности должны быть не
окончательными
.
Однако, как объясняется в этой статье , тип записи Java определяется следующим образом:
- связанный класс Java является
окончательным
, - существует только один конструктор, который принимает все атрибуты,
- атрибуты записи Java являются
окончательными
.
Таким образом, в соответствии со спецификациями JPA запись Java не может использоваться в качестве сущности.
Более того, даже если бы Hibernate смягчил эти требования, запись Java не соответствовала бы цели преобразования переходов состояний сущностей в операторы SQL.
Объекты, управляемые контекстом сохранения, которые должны быть изменяемыми, чтобы во время очистки механизм проверки на наличие ошибок мог генерировать инструкции ОБНОВЛЕНИЯ.
Hibernate уже поддерживает объекты только для чтения с помощью аннотации @Immutable , но классы сущностей и атрибуты должны быть не окончательными. В противном случае было бы невозможно получить ленивые ассоциации по требованию .
Запись Java не подходит для использования в качестве объекта JPA или гибернации.
Использование записей Java в качестве DTO
Давайте предположим, что у нас есть следующие классы Post Info
и AuditInfo
DTO:
С помощью записей Java мы можем определить Информацию об аудите
следующим образом:
public record AuditInfo( LocalDateTime createdOn, String createdBy, LocalDateTime updatedOn, String updatedBy ) {}
и Информация о публикации
выглядит следующим образом:
public record PostInfo( Long id, String title, AuditInfo auditInfo ) {}
Теперь, чтобы использовать простое имя класса вместо полного имени в запросах JPQL, мы собираемся зарегистрировать записи AuditInfo
и PostInfo
Java с помощью ClassImportIntegrator
, предоставленного проектом Hibernate Types :
properties.put( "hibernate.integrator_provider", (IntegratorProvider) () -> Collections.singletonList( new ClassImportIntegrator( List.of( AuditInfo.class, PostInfo.class ) ) ) );
Для получения более подробной информации об Интеграторе импорта классов
утилите ознакомьтесь с этой статьей .
Использование записей Java в запросах выражений конструктора JPA
Чтобы получить Аудиторскую информацию
ДЛЯ проекции для данного Сообщения
, мы можем использовать следующий запрос JPQL:
AuditInfo auditInfo = entityManager.createQuery(""" select new AuditInfo ( p.createdOn, p.createdBy, p.updatedOn, p.updatedBy ) from Post p where p.id = :postId """, AuditInfo.class) .setParameter("postId", 1L) .getSingleResult();
Благодаря многострочным текстовым блокам Java запрос JPQL очень прост.
Хотя вы также можете получить Информацию об аудите
, используя как @SqlResultSetMapping , ни выражение конструктора JPQL, ни @SqlResultSetMapping
не позволяют получить PostInfo
, поскольку вам нужно передать правильно созданную AuditInfo
ссылку в PostInfo
конструкторе.
Использование записей Java с использованием преобразователя результатов гибернации
Там, где JPA терпит неудачу, на помощь приходит Hibernate. Благодаря [ Преобразователю результатов](
https://vladmihalcea.com/hibernate-resulttransformer/
) Функция гибернации, вы можете получить записи Java
PostInfo и
AuditInfo вместе:
ListpostInfos = entityManager.createQuery(""" select p.id, p.title, p.createdOn, p.createdBy, p.updatedOn, p.updatedBy from Post p order by p.id """) .unwrap(Query.class) .setResultTransformer( (ListResultTransformer) (tuple, aliases) -> { int i =0; return new PostInfo( ((Number) tuple[i++]).longValue(), (String) tuple[i++], new AuditInfo( (LocalDateTime) tuple[i++], (String) tuple[i++], (LocalDateTime) tuple[i++], (String) tuple[i++] ) ); } ) .getResultList();
Благодаря утилите List ResultTransformer , которая также предлагается проектом amazing Hibernate Types , мы можем использовать функцию Java Lambda для определения логики Hibernate ResultTransformer
.
Для получения более подробной информации о проекциях DTO с JPA и Hibernate ознакомьтесь с этой статьей .
Вывод
Таким образом, хотя записи Java не подходят для сопоставления объектов JPA и Hibernate, они очень полезны для прогнозов.
Получение DTO-это очень распространенное требование для данных только для чтения, которые необходимо передавать на уровень пользовательского интерфейса, поэтому записи Java могут упростить процесс определения проекций DTO.