Публикация ассоциации подресурсов @OneToMany в Spring Data REST

103

В настоящее время у меня есть приложение Spring Boot, использующее Spring Data REST. У меня есть объект домена, Postкоторый имеет @OneToManyотношение к другому объекту домена Comment. Эти классы структурированы следующим образом:

Post.java:

@Entity
public class Post {

    @Id
    @GeneratedValue
    private long id;
    private String author;
    private String content;
    private String title;

    @OneToMany
    private List<Comment> comments;

    // Standard getters and setters...
}

Комментарий.java:

@Entity
public class Comment {

    @Id
    @GeneratedValue
    private long id;
    private String author;
    private String content;

    @ManyToOne
    private Post post;

    // Standard getters and setters...
}

Их репозитории Spring Data REST JPA являются базовыми реализациями CrudRepository:

PostRepository.java:

public interface PostRepository extends CrudRepository<Post, Long> { }

CommentRepository.java:

public interface CommentRepository extends CrudRepository<Comment, Long> { }

Точка входа в приложение - это стандартное простое приложение Spring Boot. Настроено все стоково.

Application.java

@Configuration
@EnableJpaRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application {

    public static void main(final String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Кажется, все работает правильно. Когда я запускаю приложение, все работает правильно. Я могу POST новый объект Post http://localhost:8080/postsтак:

Тело: {"author":"testAuthor", "title":"test", "content":"hello world"}

Результат на http://localhost:8080/posts/1:

{
    "author": "testAuthor",
    "content": "hello world",
    "title": "test",
    "_links": {
        "self": {
            "href": "http://localhost:8080/posts/1"
        },
        "comments": {
            "href": "http://localhost:8080/posts/1/comments"
        }
    }
}

Однако, когда я выполняю GET в, http://localhost:8080/posts/1/commentsя получаю пустой объект {}, и если я пытаюсь отправить комментарий к тому же URI, я получаю HTTP 405 Method Not Allowed.

Как правильно создать Commentресурс и связать его с ним Post? Я бы хотел, http://localhost:8080/commentsесли возможно, избежать POSTing напрямую .

ccampo
источник
9
7 дней спустя и все еще не повезло. Если кто-нибудь знает, как заставить такое поведение работать, дайте мне знать. Спасибо!
ccampo
вы используете @RepositoryRestResource или контроллер? Было бы полезно увидеть и этот код.
Магнус Ласси
Я использую остаток данных загрузки Spring, у меня это сработало http://stackoverflow.com/questions/37902946/add-item-to-the-collection-with-foreign-key-via-rest-call
Taimur

Ответы:

47

Вы должны сначала опубликовать комментарий, а при публикации комментария вы можете создать объект связи сообщений.

Это должно выглядеть примерно так:

http://{server:port}/comment METHOD:POST

{"author":"abc","content":"PQROHSFHFSHOFSHOSF", "post":"http://{server:port}/post/1"}

и он будет работать отлично.

Четан Кокил
источник
2
Это сработало для меня. Просто убедитесь, что author.postэто доступно для записи (например, с помощью установщика или @JsonValueаннотации)
scheffield
1
Должно ли это также работать с запросом исправления, как при перемещении комментария из одного сообщения в другое?
aycanadal
2
Это был бы мой (значительно) предпочтительный подход, но, похоже, он не работает для меня. :( Он создает комментарий, но не создает строку в таблице разрешения (POST_COMMENTS). Есть предложения по устранению проблемы?
Banncee
3
Каков будет подход для сценария, например, с объектами «Место проведения» и «Адрес», где место проведения должно иметь адрес, а адрес ДОЛЖЕН быть связан с объектом? Я имею в виду ... чтобы избежать создания потерянного адреса, который никогда не может быть привязан ни к чему? Возможно, я ошибаюсь, но клиентское приложение НИКОГДА НЕ ДОЛЖНО нести ответственность за поддержание согласованности в базе данных. Я не могу полагаться на то, что клиентское приложение создаст адрес, а затем определенно назначит объект. Есть ли способ отправить подресурс (в данном случае объект Address) POST с созданием фактического ресурса, чтобы я мог избежать несогласованности?
apostrophedottilde
2
Я пытаюсь сделать это ( см. Здесь ), но по какой-то причине создается только ресурс, а не ассоциация.
displayname
55

Предполагая, что вы уже обнаружили URI сообщения и, следовательно, URI ресурса ассоциации (который рассматривается $association_uriниже), обычно выполняются следующие шаги:

  1. Откройте для себя коллекцию ресурсов управления комментариями:

    curl -X GET http://localhost:8080
    
    200 OK
    { _links : {
        comments : { href : "…" },
        posts :  { href : "…" }
      }
    }
    
  2. Перейдите по commentsссылке и POSTсвоим данным на ресурс:

    curl -X POST -H "Content-Type: application/json" $url 
    {  // your payload // … }
    
    201 Created
    Location: $comment_url
    
  3. Назначьте комментарий к публикации, выдав PUTURI ассоциации.

    curl -X PUT -H "Content-Type: text/uri-list" $association_url
    $comment_url
    
    204 No Content
    

Обратите внимание, что на последнем шаге, согласно спецификации text/uri-list, вы можете отправить несколько URI, идентифицирующих комментарии, разделенных разрывом строки, чтобы назначить несколько комментариев одновременно.

Еще несколько замечаний по общим дизайнерским решениям. Пример сообщения / комментария обычно является отличным примером для агрегата, что означает, что я бы избегал обратной ссылки с Commentна, Postа также CommentRepositoryполностью избегал . Если комментарии не имеют собственного жизненного цикла (чего они обычно не имеют в отношении стиля композиции), вы скорее получите комментарии, отображаемые непосредственно в строке, и весь процесс добавления и удаления комментариев может быть решен с помощью Патч JSON . Spring Data REST добавил поддержку этого в последнем выпуске-кандидате для предстоящей версии 2.2.

Оливер Дротбом
источник
4
Интересно, вот здесь нижние избиратели, по какой причине были голоса;).
Оливер Дротбом 02
3
Я не уверен насчет проигравших ... У меня даже нет репутации, чтобы сделать это! Причина, по которой мне не всегда нравится помещать комментарии в сообщения, заключается в том, что рассмотрите (маловероятный) сценарий, когда у меня есть тысячи комментариев для одного сообщения. Я хотел бы иметь возможность разбивать на страницы коллекцию комментариев вместо того, чтобы получать их всю кучу каждый раз, когда я хочу получить доступ к содержимому сообщения.
ccampo
25
Наиболее интуитивно понятный для меня способ разместить комментарий - это отправить POST на localhost: 8080 / posts / 1 / comments . Разве это не самый простой и значимый способ сделать это? И в то же время у вас по-прежнему должна быть возможность иметь специальный репозиторий комментариев. Это пружина или стандарт HAL не позволяют этого?
aycanadal
4
@OliverGierke. Это все еще рекомендуемый / единственный способ сделать это? Что делать, если дочерний элемент не допускает значения NULL ( @JoinColumn(nullable=false))? Было бы невозможно сначала выполнить POST дочернюю, а затем PUT / PATCH родительскую ассоциацию.
JW Lim
2
Есть ли руководство по использованию API, созданного с помощью Spring Data Rest? Я гуглил 2 часа и ничего не нашел. Спасибо!
Skeeve
2

Есть 2 типа сопоставления ассоциаций и композиции. В случае ассоциации мы использовали концепцию таблицы соединений, например

Сотрудник - с 1 по н-> Отдел

Таким образом, будут созданы 3 таблицы в случае Association Employee, Department, Employee_Department

Вам нужно только создать в своем коде EmployeeRepository. Помимо этого отображение должно быть таким:

class EmployeeEntity{

@OnetoMany(CascadeType.ALL)
   private List<Department> depts {

   }

}

Depatment Entity не будет содержать никакого сопоставления для иностранного ключа ... так что теперь, когда вы попробуете запрос POST для добавления сотрудника с отделом в один запрос json, он будет добавлен ....

Навин Гоял
источник
1

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

Сельва
источник
1
То, о чем вы говорите, легко можно сделать с помощью прогнозов
kboom
0

Для сопоставления oneToMany просто создайте POJO для этого класса, который вы хотите сопоставить, и аннотацию @OneToMany к нему, и внутренне он сопоставит его с этим идентификатором таблицы.

Кроме того, вам необходимо реализовать интерфейс Serializable для класса, который вы получаете данные.

Солнечная Чауразия
источник