Как восстановить отношения объектов после сохранения?

Я разрабатываю веб-сервис RESTful с spring-data в качестве уровня доступа к данным, поддерживаемый JPA/Hibernate. Очень часто существуют отношения между объектами домена. Например, представьте себе объект Product , в котором есть объект Category .

Теперь, когда клиент POST представляет собой Product представление для метода JAX-RS. Этот метод аннотируется с помощью @Transactional , чтобы обернуть каждую операцию репозитория в транзакцию. Конечно, клиент отправляет id уже существующего Category , а не целое представление, просто ссылку (внешний ключ).

В этом методе, если я это сделаю:

entity = repository.save(entity);

переменная entity теперь имеет Category только с полем id . Меня это не удивило. Я не ожидал сохранения (вставка SQL) для извлечения информации о связанных объектах. Но мне нужно, чтобы весь объект Product и связанные с ним объекты могли вернуться к пользователю.

Тогда я сделал это:

entity = repository.save(entity);
entity = repository.findOne(entity.getId());

то есть извлекать объект после его сохранения, внутри той же транзакции/сессии .

К моему удивлению, переменная entity ничего не изменила. На самом деле, база данных даже не получила ни одного запроса выбора. Это связано с кэшем Hibernate. По какой-то причине, когда в той же транзакции поиск не извлекает весь граф объекта, если этот объект был ранее сохранен.

With Hibernate, the solution appears to be to use session.refresh(entity) (see this and this). Makes sense.

Но как я могу добиться этого с помощью данных весны?

Я бы хотел избежать создания повторяющихся пользовательских репозиториев. Я думаю, что эта функциональность должна быть частью весенних данных itslef (некоторые люди уже сообщали об этом на форуме весенних данных: thread1 , thread2 ).

4
nl ja de

1 ответы

<Сильный> ТЛ, д-р

Ссылки между объектами в веб-слое должны быть ясными с использованием ссылок и не должны скрываться за экземплярами полупоставленных объектов. Ссылки в уровне персистентности представлены ссылками на объекты. Таким образом, должен быть выделенный шаг, преобразующий один (ссылка) в другой (полностью заполненная ссылка на объект).

<Сильный> Подробнее

Это анти-шаблон, чтобы обмениваться бэкэндами как таковыми и предполагать, что привязка маршалинга делает правильную вещь. Таким образом, клиенты должны работать со ссылками и передавать их на сервер, чтобы указать, что они хотят установить соединение между уже существующим ресурсом и тем, который должен быть создан.

Предположим, что у вас есть существующая Category , которая отображается через /categories/4711 , вы можете отправить сообщение на свой сервер:

POST /products
{ links : [ { rel : "category", href : "/categories/4711" } ],
 //further product data
}

Сервер создаст экземпляр нового экземпляра Product , заполнит его дополнительными данными и, в конце концов, заполнит ассоциации следующим образом:

  1. Определите свойства, которые нужно заполнить, просматривая типы отношений ссылок (например, свойство category ).
  2. Извлеките идентификатор бэкэнд из данного URI
  3. Используйте соответствующий репозиторий для поиска связанного экземпляра объекта
  4. Установить его на корневой объект

Итак, в вашем примере кипятите вниз:

Product product = new Product();
// populate primitive properties
product.setCategory(categoryRepository.findOne(4711));
productRepository.save(product);

Просто разместите на сервере что-то вроде этого:

POST /products
{ category : {
    id : 1, … },
  … 
}

является субоптимальным по многим причинам:

  1. Вы хотите, чтобы поставщик сохранения неявно сохранял экземпляр Product и в то же время «узнавал», что упомянутый экземпляр Category (фактически состоящий только из идентификатора) не предназначено для сохранения, но обновляется с данными уже существующего Category ? Это довольно немного магии, о которой я бы сказал.
  2. Вы по существу накладываете структуру данных, которую используете для POST на сервер, на уровень персистентности, ожидая, что он будет прозрачно разбираться с тем, как вы решили делать POST. Это не ответственность слоя с сохранением, а веб-слоя. Вся цель веб-уровня состоит в том, чтобы смягчить характеристики HTTP-протокола, используя представления и ссылки на бэкэнд-сервис.
1
добавлено
Отличный ответ! Разве мы уже не связываем бэкэнд-идентификаторы в единичных URL-адресах, например /categories/4711 ?
добавлено автор miguelcobain, источник
Кроме того, нам нужно проанализировать связанный URL-адрес категории и получить его идентификатор. Это должно быть предоставлено некоторыми помощниками или библиотекой HATEOAS. Не знаю, как я могу это сделать элегантно. HATEOAS кажется более обширным, чем я думал. :)
добавлено автор miguelcobain, источник
Я вижу, что вы являетесь вкладчиком весенней ненависти. Можете ли вы сказать мне, насколько хорошо он поддерживает JAX-RS, особенно в отношении ResourceAssemblers? Есть ли какой-нибудь пример с JAX-RS? У меня возникают некоторые проблемы в JaxRsLinkBuilder, когда он пытается выполнить новый JaxRsLinkBuilder (ServletUriComponentsBuilder.fromCurrentServ & zwnj; letMapping ()) . Я получаю Не удалось найти текущий запрос через RequestContextHolder . Я пытался использовать эту библиотеку в прошлом. Похоже, что он очень хорошо относится к HATEOAS, но так и не смог заставить его работать с JAX-RS. Просто нашел примерный репозиторий с регулярными счетчиками Spring Contollers
добавлено автор miguelcobain, источник
Я не использовал последние весенние ненависти. Я не использовал EntityLinks. Возможно ли @EnableEntityLinks в xml? Я немного потерял, как использовать программную конфигурацию в приложении Spring + JAX-RS. Какой «WebInitializer» следует подклассу/реализовать? Возможно, я должен создать новый вопрос SO.
добавлено автор miguelcobain, источник
Я использовал /categories/4711 как читаемый пример, но URI мог быть /fe6325a27 , что делает очевидным дополнительный шаг сопоставления. В Spring HATEOAS у нас уже есть API для создания экземпляров Link , указывающих на контроллеров и ресурсов JAX-RS. К сожалению, вы единственный, кто знает, где в URI вы найдете идентификатор backend (например, /categories/4711 VS. /categories? Id = 4711 . может потребоваться полный шаг отображения, как показано выше. Поэтому для этого сложно создать общую поддержку.
добавлено автор Oliver Gierke, источник
Короче: поддержки xml еще нет, JavaConfig - это путь. Дальнейшие вопросы SO, вероятно, являются хорошей идеей. Как правило, вы можете взглянуть на Spring RESTBucks, чтобы узнать, как это может работать ... github.com/olivergierke/ весна-restbucks
добавлено автор Oliver Gierke, источник
pro.jvm
pro.jvm
3 503 участник(ов)

Сообщество разработчиков Java Scala Kotlin Groovy Clojure Чат для нач-их: @javastart Наш сайт: projvm.com projvm.ru Наш канал: @proJVM Вакансии: @jvmjobs Конфы: @jvmconf

Java & Co
Java & Co
2 370 участник(ов)

Можно обсуждать с матом и без всё, что касается жабы, вплоть до холиваров. НЕ ИМЕЕТ ОТНОШЕНИЯ К САЙТУ JAVARUSH.RU ПРАВИЛА - https://t.me/javarush/75723 Вакансии сюда - https://telegram.me/joinchat/B7IzvUCnfo6d8t3yIxKguQ По вопросам - @thedude

learn.java
learn.java
1 888 участник(ов)

Чат для начинающих и не только Статистика: https://combot.org/chat/-1001083535868 Основной чат - @jvmchat

Spring Framework and more
Spring Framework and more
839 участник(ов)

чат о spring framework и связанных с ним технологиях. We're discussing: job, tech questions, beer meet up/networking: tech review ,LinkedIn skills, SOF q/a raise up& etc. languages: russian,java,eng.

Java Underground
Java Underground
169 участник(ов)

https://vk.com/javatutorial

Javanese Questions
Javanese Questions
109 участник(ов)

Чат предназначен для обмена знаниями строго в формате в вопрос-ответ. Тема — Java, Kotlin и Android. Вопрос должен быть предварительно прогуглен, понятно и грамотно сформулирован, помечен хэштегами. Ответ — тем более. Куски кода размером в несколько строк можно писать прямо здесь, для больших кусков кода стоит использовать http://gist.github.com/, http://pastebin.com/, https://codeshare.io/ или любой аналогичный сервис. В некоторых случаях можно прикреплять скриншоты. Стикеры и гифки запрещены. Дополнять и уточнять вопросы и ответы — редактированием исходного сообщения. Обсуждения должны приводить к редактированию вопроса/ответа и удаляться. По хештегам можно искать существующие вопросы и овтеты: #вопрос #ответ #git #generics #java #server #awt #javafx #swing #kotlin #anko #tornadofx #ktor #android #recyclerView #performance #arch #network #permissions #storage #async