Исключение LazyInitializationException и DTO в JAX-RS

#hibernate #rest #jax-rs #entity #dto

#переход в спящий режим #отдых #jax-rs #сущность #dto

Вопрос:

В моем бэкэнд-приложении RESTful есть две сущности (A и B) со следующими отношениями: одна сущность A может быть связана со многими сущностями B, поэтому в A у нас есть коллекция объектов B, помеченных @OneToMany.

Реализуя шаблон MVC, у меня есть:

  • Уровень ресурсов (интерфейс между пользователем / интерфейсом и моим приложением)
  • Уровень обслуживания (ejb, которые вызывают DAO для выполнения запроса в БД)

У меня было исключение LazyInitializationException при извлечении объекта A на уровне ресурсов. Поискав вокруг, я понял, что проблема проистекает из того факта, что, когда уровень сервиса возвращает объект A, он отсоединяет его от уровня сохраняемости, и поскольку по умолчанию коллекции загружаются ЛЕНИВО, если я пытаюсь получить их на уровне ресурсов, я получаю исключение.

Одним из наиболее популярных решений по сети является использование объектов DTO и преобразование объектов на уровне сервиса в DTO.

Мой первый вопрос связан с этим последним отрывком: является ли этот подход просто уловкой для использования методов «get» объекта и, таким образом, принуждения ORM к загрузке коллекции? Я говорю это потому, что когда мы сопоставляем объект с DTO, мы выполняем следующую операцию:

 objectADTO.setB(objectAEntity.getB());
  

Используя эту операцию, мы извлекаем из базы данных коллекцию B. Мы могли бы достичь того же результата, просто написав строку кода, подобную этой, на уровне сервиса: objectAEntity.getB() , а затем напрямую вернуть на уровень сервиса objectAEntity, который не вызовет исключения, поскольку мы использовали .getB() для извлечения фактических значений.

Теперь вы могли бы сказать: объект DTO позволяет вам обладать большей универсальностью, но тогда мой второй вопрос таков: должен ли я определять другое сопоставление между сущностью A и DTO A на основе методов на уровне обслуживания, которые мне нужны? Другими словами, если у меня есть два метода, которые возвращают разную информацию о A, должен ли я обрабатывать два вида отображения DTO? (рассмотрим, например, что A содержит две разные коллекции изображений, и методу 1 требуется первая коллекция, а второму методу требуется вторая. Я не могу использовать одно и то же отображение DTO, которое предоставляет все коллекции для обоих методов, потому что это вернет слишком тяжелый объект)

Я надеюсь, что я был ясен при описании моих проблем.

Заранее спасибо за ответы

Ответ №1:

Вы можете реализовать DTO таким образом, чтобы отображать только свойства Java и запускать отложенную загрузку во время преобразования, но это может привести к печально известной проблеме с отложенной загрузкой N 1. Было бы намного эффективнее загружать только те данные, которые действительно нужны DTO.

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

должен ли я определять другое сопоставление между сущностью A и DTO A на основе методов на уровне обслуживания, которые мне нужны? Другими словами, если у меня есть два метода, которые возвращают разную информацию о A, должен ли я обрабатывать два вида отображения DTO?

Да, каждый вариант использования должен иметь свой собственный DTO. Вы могли бы добиться повторного использования с помощью наследования или композиции, если вам это нужно.

Это идеальный вариант использования для представлений объектов с сохранением блейза.

Я создал библиотеку, позволяющую легко сопоставлять модели JPA с моделями, определяемыми пользовательским интерфейсом или абстрактным классом, что-то вроде весенних прогнозов данных на стероидах. Идея заключается в том, что вы определяете свою целевую структуру (модель домена) так, как вам нравится, и сопоставляете атрибуты (геттеры) через выражения JPQL с моделью сущности.

Модель DTO для вашего варианта использования может выглядеть следующим образом с помощью представлений объектов Blaze-Persistence:

 @EntityView(A.class)
public interface ADto {
    @IdMapping
    Long getId();
    String getName();
    @Mapping("bCollection")
    Set<BDto> getBs();

    @EntityView(B.class)
    interface BDto {
        @IdMapping
        Long getId();
        String getName();
    }
}
  

Запрос — это вопрос применения представления сущности к запросу, простейшим из которых является просто запрос по идентификатору.

ADto a = entityViewManager.find(entityManager, ADto.class, id);

Интеграция данных Spring позволяет использовать ее почти как проекции данных Spring: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Комментарии:

1. Спасибо, Кристиан, очень четкое объяснение. Для меня звучит странно создавать много DTO для одного и того же объекта, но это имеет смысл, особенно если они содержат изображения или LOB в целом. Я подожду несколько дней, чтобы проверить ваш ответ, просто чтобы посмотреть, захочет ли кто-нибудь еще ответить.

2. Почему вы думаете, что это странно? У вас разные варианты использования, так почему бы не иметь разные DTO? Как только вы хотите отобразить объект в таблице, затем вы хотите отобразить его во входных данных select, а затем, как только вы захотите показать объект в целом. Три разных варианта использования, где каждый заслуживает пользовательского DTO, потому что вы показываете разные данные. Повторное использование одного и того же DTO является пустой тратой: blog.jooq.org/2017/03/08 /…