OPUBLIKOWANIE powiązania zasobu podrzędnego @OneToMany w Spring Data REST


103

Obecnie posiadam aplikację Spring Boot używającą Spring Data REST. Mam podmiot domeny Post, która ma @OneToManyzwiązek z innym podmiotem domeny Comment. Klasy te mają następującą strukturę:

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...
}

Comment.java:

@Entity
public class Comment {

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

    @ManyToOne
    private Post post;

    // Standard getters and setters...
}

Ich repozytoria Spring Data REST JPA to podstawowe implementacje CrudRepository:

PostRepository.java:

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

CommentRepository.java:

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

Punktem wejścia aplikacji jest standardowa, prosta aplikacja Spring Boot. Wszystko jest skonfigurowane w magazynie.

Application.java

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

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

Wszystko wydaje się działać poprawnie. Po uruchomieniu aplikacji wszystko wydaje się działać poprawnie. Mogę OPUBLIKOWAĆ nowy obiekt posta, aby http://localhost:8080/posts:

Ciało: {"author":"testAuthor", "title":"test", "content":"hello world"}

Wynik w 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"
        }
    }
}

Jednak gdy wykonuję GET w http://localhost:8080/posts/1/comments, otrzymuję {}zwracany pusty obiekt i jeśli spróbuję POSTOWAĆ komentarz do tego samego identyfikatora URI, otrzymuję metodę HTTP 405 niedozwoloną.

Jaki jest właściwy sposób tworzenia Commentzasobu i kojarzenia go z tym Post? Chciałbym uniknąć wysyłania postów bezpośrednio do, http://localhost:8080/commentsjeśli to możliwe.


9
7 dni później i nadal nie ma szczęścia. Jeśli ktoś zna sposób, aby to zachowanie zadziałało, daj mi znać. Dzięki!
— ccampo

czy używasz @RepositoryRestResource czy kontrolera? Przydałoby się również zobaczyć ten kod.
— Magnus Lassi

Odpowiedzi:


47

Najpierw musisz opublikować komentarz, a podczas publikowania komentarza możesz utworzyć jednostkę postów asocjacyjnych.

Powinien wyglądać jak poniżej:

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

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

i będzie działać idealnie.


2
To zadziałało dla mnie. Tylko upewnij się, że author.postmożna zapisywać (na przykład ustawiając lub @JsonValueadnotację)
— scheffield

1
Czy powinno to działać również w przypadku żądania poprawki, tak jak przy przenoszeniu komentarza z jednego postu do drugiego?
— aycanadal

2
Byłoby to moje (bardzo) preferowane podejście, ale wydaje się, że nie działa. :( Tworzy komentarz, ale nie tworzy wiersza w tabeli rozdzielczości (POST_COMMENTS). Jakieś sugestie, jak rozwiązać?
— banncee

3
Jakie byłoby podejście do scenariusza, np. Z obiektami Miejsce i Adres, gdzie miejsce musi mieć adres, a adres MUSI być powiązany z miejscem? To znaczy ... żeby uniknąć tworzenia osieroconego adresu, który nigdy nie może być do niczego przypisany? Może się mylę, ale aplikacja kliencka NIGDY NIE POWINNA być odpowiedzialna za utrzymanie spójności w bazie danych. Nie mogę polegać na tym, że aplikacja kliencka utworzy adres, a następnie ostatecznie przypisze do miejsca. Czy istnieje sposób na POST zasobu podrzędnego (w tym przypadku encji Adres) wraz z tworzeniem rzeczywistego zasobu, aby uniknąć niespójności?
— apostrophedottilde

2
Próbuję to zrobić ( patrz tutaj ), ale z jakiegoś powodu tworzony jest tylko zasób, a nie skojarzenie.
— displayname

55

Zakładając, że już odkryłeś identyfikator URI wiadomości, a tym samym identyfikator URI zasobu asocjacji (uważany za $association_uriponiżej), zazwyczaj wykonuje się następujące kroki:

  1. Odkryj komentarze do zarządzania zasobami kolekcji:

    curl -X GET http://localhost:8080
    
    200 OK
    { _links : {
        comments : { href : "…" },
        posts :  { href : "…" }
      }
    }
  2. Skorzystaj z commentslinku i POSTswoich danych do zasobu:

    curl -X POST -H "Content-Type: application/json" $url 
    { … // your payload // … }
    
    201 Created
    Location: $comment_url
  3. Przypisz komentarz do posta, wydając PUTidentyfikator URI skojarzenia.

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

Zwróć uwagę, że w ostatnim kroku, zgodnie ze specyfikacją text/uri-list, możesz przesłać wiele identyfikatorów URI identyfikujących komentarze oddzielone znakiem końca wiersza, aby przypisać wiele komentarzy jednocześnie.

Jeszcze kilka uwag na temat ogólnych decyzji projektowych. Przykład postu / komentarzy jest zwykle świetnym przykładem agregacji, co oznacza, że ​​unikałbym odniesienia wstecznego od Commentdo, Posta także CommentRepositorycałkowicie unikałbym . Jeśli komentarze nie mają własnego cyklu życia (czego zwykle nie mają w relacji w stylu kompozycji), wolisz je renderować bezpośrednio w tekście, a cały proces dodawania i usuwania komentarzy można raczej przeprowadzić za pomocą Poprawka JSON . Spring Data REST dodał obsługę tego w najnowszej wersji kandydata na nadchodzącą wersję 2.2.


4
Interesuje się tutaj ze strony słabszych wyborców, jaki był powód głosowania;).
— Oliver Drotbohm

3
Nie jestem pewien co do wyborców w dół ... Nie mam nawet reputacji, aby to zrobić! Powodem, dla którego niekoniecznie lubię umieszczać komentarze w postach, jest rozważenie (mało prawdopodobnego) scenariusza, w którym mam tysiące komentarzy do jednego postu. Chciałbym mieć możliwość podzielenia kolekcji komentarzy na strony zamiast pobierania całej ich liczby za każdym razem, gdy chcę uzyskać dostęp do treści posta.
— ccampo

25
Najbardziej intuicyjnym sposobem publikowania komentarzy jest wysłanie POST na localhost: 8080 / posts / 1 / comments . Czy nie jest to najprostszy i najbardziej znaczący sposób na zrobienie tego? Jednocześnie nadal powinieneś mieć możliwość posiadania dedykowanego repozytorium komentarzy. Czy to standard sprężyny czy HAL na to nie pozwala?
— aycanadal

4
@OliverGierke Czy jest to nadal zalecany / jedyny sposób, aby to zrobić? Co się stanie, jeśli dziecko nie może mieć wartości null ( @JoinColumn(nullable=false))? Nie byłoby możliwe najpierw OPUBLIKOWANIE dziecka, a następnie PUT / PATCH powiązanie nadrzędne.
— JW Lim

2
Czy istnieje przewodnik dotyczący korzystania z interfejsu API utworzonego za pomocą sprężynowego spoczynku danych? Przeszukałem go przez 2 godziny i nic nie znalazłem. Dziękuję Ci!
— Skeeve

2

Istnieją 2 typy mapowania: asocjacja i kompozycja. W przypadku asocjacji zastosowaliśmy koncepcję łączenia stołu, taką jak

Pracownik - 1 do n-> Dział

W związku z tym zostaną utworzone 3 tabele w przypadku Pracownik Stowarzyszenia, Departament, Dział_Pracownik

Musisz tylko utworzyć EmployeeRepository w swoim kodzie. Poza tym mapowanie powinno wyglądać tak:

class EmployeeEntity{

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

   }

}

Jednostka działu nie będzie zawierała żadnego mapowania dla klucza obcego ... więc teraz, gdy spróbujesz wysłać żądanie POST o dodanie pracownika z działem w pojedynczym żądaniu json, zostanie ono dodane ....


1

Zmierzyłem się z tym samym scenariuszem i musiałem usunąć klasę repozytorium dla jednostki podrzędnej, ponieważ użyłem jednego do wielu mapowań i wyciągnąłem dane przez samą jednostkę główną. Teraz otrzymuję całą odpowiedź z danymi.


1
To, o czym mówisz, można łatwo zrobić za pomocą prognoz
— kboom

0

W przypadku mapowania oneToMany, po prostu utwórz POJO dla tej klasy, którą chcesz mapować, i adnotację @OneToMany do niej, a wewnętrznie zamapuje to na ten identyfikator tabeli.

Ponadto musisz zaimplementować interfejs Serializable do klasy, dla której pobierasz dane.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.