POST di un'associazione di sottorisorse @OneToMany in Spring Data REST


103

Al momento ho un'applicazione Spring Boot che utilizza Spring Data REST. Ho un soggetto dominio Postche ha la @OneToManyrelazione ad un altro soggetto del dominio, Comment. Queste classi sono strutturate come segue:

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

I loro repository Spring Data REST JPA sono implementazioni di base di CrudRepository:

PostRepository.java:

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

CommentRepository.java:

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

Il punto di ingresso dell'applicazione è una semplice applicazione Spring Boot standard. Tutto è configurato stock.

Application.java

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

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

Tutto sembra funzionare correttamente. Quando eseguo l'applicazione, tutto sembra funzionare correttamente. Posso POSTARE un nuovo oggetto Post in questo http://localhost:8080/postsmodo:

Corpo: {"author":"testAuthor", "title":"test", "content":"hello world"}

Risultato a 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"
        }
    }
}

Tuttavia, quando eseguo un GET su http://localhost:8080/posts/1/commentsottengo {}restituito un oggetto vuoto e se provo a POST un commento allo stesso URI, ottengo un metodo HTTP 405 non consentito.

Qual è il modo corretto per creare una Commentrisorsa e associarla a questa Post? Vorrei evitare di POSTARE direttamente a, http://localhost:8080/commentsse possibile.


9
7 giorni dopo e ancora senza fortuna. Se qualcuno conosce un modo per far funzionare questo comportamento, fatemelo sapere. Grazie!
ccampo

stai usando @RepositoryRestResource o un controller? Sarebbe utile vedere anche quel codice.
Magnus Lassi

Risposte:


47

Devi prima pubblicare il commento e mentre pubblichi il commento puoi creare un'entità post dell'associazione.

Dovrebbe assomigliare di seguito:

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

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

e funzionerà perfettamente.


2
Questo ha funzionato per me. Assicurati solo che author.postsia scrivibile (ad esempio avendo un setter o @JsonValueun'annotazione)
scheffield

1
Questo dovrebbe funzionare anche con una richiesta di patch come per spostare il commento da un post all'altro?
aycanadal

2
Questo sarebbe il mio approccio (ampiamente) preferito, ma non sembra funzionare per me. :( Crea il commento, ma non crea la riga nella tabella di risoluzione (POST_COMMENTS). Qualche suggerimento su come risolvere?
banncee

3
Quale sarebbe l'approccio per uno scenario, ad esempio con entità Sede e Indirizzo, in cui una sede deve avere un indirizzo e un indirizzo DEVE essere associato a una sede? Voglio dire ... per evitare di creare un indirizzo orfano che potrebbe non essere mai assegnato a nulla? Forse sbaglio, ma l'app client NON DOVREBBE MAI essere responsabile del mantenimento della coerenza all'interno del database. Non posso fare affidamento sul fatto che l'app client crei un indirizzo e quindi lo assegni definitivamente a una sede. C'è un modo per POST della sotto-risorsa (in questo caso l'entità Indirizzo) con la creazione della risorsa effettiva in modo da evitare incongruenze ??
apostrofoottilde

2
Provo a farlo ( vedi qui ) ma per qualche motivo viene creata solo la risorsa, non l'associazione.
displayname

55

Supponendo che tu abbia già scoperto l'URI del post e quindi l'URI della risorsa dell'associazione (considerata di $association_uriseguito), generalmente esegue questi passaggi:

  1. Scopri la risorsa raccolta che gestisce i commenti:

    curl -X GET http://localhost:8080
    
    200 OK
    { _links : {
        comments : { href : "…" },
        posts :  { href : "…" }
      }
    }
  2. Segui il commentslink e i POSTtuoi dati alla risorsa:

    curl -X POST -H "Content-Type: application/json" $url 
    {  // your payload // … }
    
    201 Created
    Location: $comment_url
  3. Assegna il commento al post emettendo un PUTURL dell'associazione.

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

Si noti che nell'ultimo passaggio, in base alla specifica di text/uri-list, è possibile inviare più URI che identificano i commenti separati da un'interruzione di riga per assegnare più commenti contemporaneamente.

Qualche nota in più sulle decisioni generali di progettazione. Un esempio di post / commenti è di solito un ottimo esempio per un aggregato, il che significa che eviterei il riferimento a ritroso da Commenta a Posted eviterei anche CommentRepositorycompletamente. Se i commenti non hanno un ciclo di vita da soli (cosa che di solito non hanno in una relazione di stile di composizione), preferisci che i commenti vengano renderizzati direttamente in linea e l'intero processo di aggiunta e rimozione dei commenti può piuttosto essere gestito utilizzando Patch JSON . Spring Data REST ha aggiunto il supporto per questo nell'ultima release candidate per la prossima versione 2.2.


4
Interessato a qui dagli elettori giù, qual è stato il motivo dei voti;).
Oliver Drotbohm

3
Non sono sicuro degli elettori a terra ... non ho nemmeno la reputazione di farlo! Il motivo per cui non mi piace necessariamente mettere i commenti in linea con i post è perché considera lo scenario (improbabile) in cui ho migliaia di commenti per un singolo post. Vorrei essere in grado di impaginare la raccolta di commenti invece di ottenerne l'intero gruppo ogni volta che voglio accedere ai contenuti del post.
ccampo

25
Il modo più intuitivo per me di pubblicare un commento è creare un POST su localhost: 8080 / posts / 1 / comments . Non è il modo più semplice e significativo per farlo? E allo stesso tempo, dovresti comunque essere in grado di avere un repository di commenti dedicato. È la primavera o lo standard HAL che non lo consente?
aycanadal

4
@OliverGierke È ancora l'unico modo / consigliato per farlo? Cosa succede se il bambino non è annullabile ( @JoinColumn(nullable=false))? Non sarebbe possibile prima POST il figlio, poi PUT / PATCH l'associazione genitore.
JW Lim

2
Esiste una guida per l'utilizzo di API create con il supporto dati a molla? Ho cercato su Google per 2 ore e non ho trovato nulla. Grazie!
Skeeve

2

Esistono 2 tipi di associazione e composizione di mappatura. In caso di associazione abbiamo utilizzato il concetto di tabella di join come

Dipendente - da 1 a n-> Dipartimento

Quindi verranno create 3 tabelle in caso di Association Employee, Department, Employee_Department

Hai solo bisogno di creare il EmployeeRepository nel tuo codice. A parte quella mappatura dovrebbe essere così:

class EmployeeEntity{

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

   }

}

Depatment Entity non conterrà alcuna mappatura per la chiave forign ... quindi ora, quando proverai la richiesta POST per l'aggiunta di Employee with Department in una singola richiesta json, verrà aggiunta ....


1

Ho affrontato lo stesso scenario e ho dovuto rimuovere la classe del repository per la sottoentità poiché ho usato una mappatura da uno a molti e ho estratto i dati attraverso l'entità principale stessa. Ora ricevo l'intera risposta con i dati.


1
Questa cosa di cui parli può essere facilmente eseguita con le proiezioni
kboom

0

Per la mappatura oneToMany, crea un POJO per la classe che desideri mappare e l'annotazione @OneToMany su di essa e internamente lo mapperà a quell'ID tabella.

Inoltre, è necessario implementare l'interfaccia Serializable per la classe che si stanno recuperando i dati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.