#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 /…