POSTER une association de sous-ressources @OneToMany dans Spring Data REST

103

Actuellement, j'ai une application Spring Boot utilisant Spring Data REST. J'ai une entité de domaine Postqui a la @OneToManyrelation à une autre entité de domaine, Comment. Ces classes sont structurées comme suit:

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

Leurs référentiels Spring Data REST JPA sont des implémentations de base de CrudRepository:

PostRepository.java:

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

CommentRepository.java:

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

Le point d'entrée de l'application est une application Spring Boot standard et simple. Tout est configuré en stock.

Application.java

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

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

Tout semble fonctionner correctement. Lorsque j'exécute l'application, tout semble fonctionner correctement. Je peux POSTER un nouvel objet Post pour http://localhost:8080/postsaimer ainsi:

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

Résultat à 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"
        }
    }
}

Cependant, lorsque j'effectue un GET à, http://localhost:8080/posts/1/commentsj'obtiens un objet vide {}renvoyé, et si j'essaie de POSTER un commentaire sur le même URI, j'obtiens une méthode HTTP 405 non autorisée.

Quelle est la bonne façon de créer une Commentressource et de l'associer à celle-ci Post? J'aimerais éviter de poster directement sur http://localhost:8080/commentssi possible.

ccampo
la source
9
7 jours plus tard et toujours pas de chance. Si quelqu'un connaît un moyen de faire fonctionner ce comportement, faites-le moi savoir. Merci!
ccampo
utilisez-vous @RepositoryRestResource ou un contrôleur? Il serait également utile de voir ce code.
Magnus Lassi
J'utilise le repos de données de démarrage de printemps, cela a fonctionné pour moi http://stackoverflow.com/questions/37902946/add-item-to-the-collection-with-foreign-key-via-rest-call
Taimur

Réponses:

47

Vous devez d'abord publier le commentaire et lors de la publication du commentaire, vous pouvez créer une entité de publication d'association.

Cela devrait ressembler à quelque chose comme ci-dessous:

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

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

et cela fonctionnera parfaitement bien.

Chetan Kokil
la source
2
Cela a fonctionné pour moi. Assurez-vous simplement que le author.postest accessible en écriture (par exemple en ayant un setter ou une @JsonValueannotation)
scheffield
1
Cela devrait-il également fonctionner avec une demande de correctif, comme pour déplacer le commentaire d'un article à un autre?
aycanadal
2
Ce serait mon approche (largement) préférée, mais cela ne semble pas fonctionner pour moi. :( Il crée le commentaire, mais ne crée pas la ligne dans la table de résolution (POST_COMMENTS). Des suggestions sur la façon de résoudre?
banncee
3
Quelle serait l'approche pour un scénario, par exemple avec des entités Lieu et Adresse, où un lieu doit avoir une Adresse et une adresse DOIT être associée à un Lieu? Je veux dire ... pour éviter de créer une adresse orpheline qui ne peut jamais être attribuée à quoi que ce soit? Peut-être que je me trompe, mais l'application cliente NE DEVRAIT JAMAIS être responsable du maintien de la cohérence dans la base de données. Je ne peux pas compter sur l'application cliente pour créer une adresse et l'attribuer définitivement à un lieu. Existe-t-il un moyen de POSTER la sous-ressource (dans ce cas, l'entité Address) avec la création de la ressource réelle afin que je puisse éviter les incohérences?
apostrophedottilde
2
J'essaye de faire ceci ( voir ici ) mais pour une raison quelconque, seule la ressource, pas l'association, est créée.
displayname
55

En supposant que vous ayez déjà découvert l'URI post et donc l'URI de la ressource d'association (considérée comme étant $association_uridans ce qui suit), il effectue généralement ces étapes:

  1. Découvrez la ressource de collection gérant les commentaires:

    curl -X GET http://localhost:8080
    
    200 OK
    { _links : {
        comments : { href : "…" },
        posts :  { href : "…" }
      }
    }
  2. Suivez le commentslien et POSTvos données vers la ressource:

    curl -X POST -H "Content-Type: application/json" $url 
    {  // your payload // … }
    
    201 Created
    Location: $comment_url
  3. Attribuez le commentaire au message en émettant un PUTà l'URI de l'association.

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

Notez qu'à la dernière étape, selon la spécification de text/uri-list, vous pouvez soumettre plusieurs URI identifiant des commentaires séparés par un saut de ligne pour affecter plusieurs commentaires à la fois.

Quelques notes supplémentaires sur les décisions générales de conception. Un exemple de publication / commentaire est généralement un excellent exemple d'agrégat, ce qui signifie que j'éviterais la référence arrière du Commentvers le Postet que j'éviterais CommentRepositorycomplètement le. Si les commentaires n'ont pas de cycle de vie par eux-mêmes (ce qu'ils n'ont généralement pas dans une relation de style composition), vous préférez obtenir les commentaires rendus directement en ligne et l'ensemble du processus d'ajout et de suppression de commentaires peut plutôt être traité en utilisant Patch JSON . Spring Data REST a ajouté la prise en charge de cela dans la dernière version candidate de la prochaine version 2.2.

Oliver Drotbohm
la source
4
Intéressé ici par les électeurs en bas, quelle était la raison des votes;).
Oliver Drotbohm
3
Je ne suis pas sûr des électeurs à la baisse ... Je n'ai même pas la réputation de le faire! La raison pour laquelle je n'aime pas nécessairement mettre des commentaires en ligne avec les messages est parce que considérons le scénario (peu probable) lorsque j'ai des milliers de commentaires pour un seul article. J'aimerais pouvoir paginer la collection de commentaires au lieu d'en obtenir l'intégralité à chaque fois que je veux accéder au contenu de l'article.
ccampo
25
Le moyen le plus intuitif pour moi de publier un commentaire est de faire un POST sur localhost: 8080 / posts / 1 / comments . N'est-ce pas la manière la plus simple et la plus significative de le faire? Et en même temps, vous devriez toujours pouvoir disposer d'un référentiel de commentaires dédié. Est-ce le ressort ou la norme HAL qui ne le permet pas?
aycanadal
4
@OliverGierke Est-ce toujours le moyen recommandé / unique de le faire? Que faire si l'enfant n'est pas nullable ( @JoinColumn(nullable=false))? Il ne serait pas possible de POSTER d'abord l'enfant, puis de PUT / PATCH l'association parent.
JW Lim
2
Existe-t-il un guide pour l'utilisation de l'API créée avec le support de données de printemps? Je l'ai googlé pendant 2 heures et je n'ai rien trouvé. Je vous remercie!
Skeeve
2

Il existe 2 types d'association et de composition de mappage. En cas d'association, nous avons utilisé le concept de table de jointure comme

Employé - 1 à n-> Département

Donc 3 tables seront créées dans le cas de l'Association Employé, Département, Employé_Département

Il vous suffit de créer le EmployeeRepository dans votre code. En dehors de cette cartographie devrait être comme ça:

class EmployeeEntity{

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

   }

}

Depatment Entity ne contiendra aucun mappage pour la clé forign ... alors maintenant, lorsque vous essaierez la demande POST pour ajouter un employé avec un service dans une seule demande json, elle sera ajoutée ....

Naveen Goyal
la source
1

J'ai été confronté au même scénario et j'ai dû supprimer la classe de référentiel pour la sous-entité car j'ai utilisé un à plusieurs mappages et extraire des données via l'entité principale elle-même. Maintenant, j'obtiens toute la réponse avec des données.

Selva
la source
1
Cette chose dont vous parlez peut être facilement réalisée avec des projections
kboom
0

Pour le mappage oneToMany, créez simplement un POJO pour la classe que vous souhaitez mapper, et une annotation @OneToMany dessus, et en interne, il le mappera à cet identifiant de table.

En outre, vous devez implémenter l'interface Serializable à la classe que vous récupérez les données.

Sunny Chaurasia
la source