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

Лучший способ использовать записи Java с JPA и гибернацией

Узнайте, как лучше всего использовать записи Java с помощью JPA и гибернации. Хотя записи не подходят для сопоставления сущностей, они являются хорошей альтернативой для проекций DTO.

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

List postInfos = 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.