Автор оригинала: 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.