在Spring Data REST中POST一个@OneToMany子资源关联

109

目前我有一个使用Spring Data REST的Spring Boot应用程序。我有一个域实体Post,它与另一个域实体Comment具有@OneToMany关系。这些类的结构如下:

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

他们的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到http://localhost:8080/posts

Body: {"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 POST评论,则会收到HTTP 405 Method Not Allowed的错误。如何正确创建一个Comment资源并将其与此Post相关联?如果可能的话,我想避免直接POST到http:// localhost:8080 / comments

9
七天过去了,仍然没有成功。如果有人知道如何让这个功能正常工作,请告诉我。谢谢! - ccampo
你是使用 @RepositoryRestResource 还是控制器(controller)?同时看到代码会更有帮助。 - Magnus Lassi
我正在使用Spring Boot Data Rest,它对我很有效。参考链接:https://dev59.com/-Zjga4cB1Zd3GeqPGCfY。 - Taimur
5个回答

56
假设您已经发现了帖子URI,因此也知道了关联资源的URI(在下文中称为$association_uri),通常需要执行以下步骤:
  1. Discover the collection resource managing comments:

     curl -X GET http://localhost:8080
    
     200 OK
     { _links : {
         comments : { href : "…" },
         posts :  { href : "…" }
       }
     }
    
  2. Follow the comments link and POST your data to the resource:

     curl -X POST -H "Content-Type: application/json" $url 
     {  // your payload //  }
    
     201 Created
     Location: $comment_url
    
  3. Assign the comment to the post by issuing a PUT to the association URI.

     curl -X PUT -H "Content-Type: text/uri-list" $association_url
     $comment_url
    
     204 No Content
    
注意,在最后一步中,根据text/uri-list的规范,您可以提交多个标识评论的URI并用换行符分隔以一次性分配多个评论。
关于一般设计决策的更多说明。帖子/评论示例通常是聚合的绝佳示例,这意味着我会避免从CommentPost的反向引用,并完全避免使用CommentRepository。如果评论本身没有生命周期(在组合式关系中通常不会有),则您可以直接内联渲染评论,并且添加和删除评论的整个过程可以通过使用JSON Patch来处理。Spring Data REST已在即将发布的2.2版本的最新发布候选版中添加了支持

5
希望能听到投反对票的人分享一下投票原因 ;). - Oliver Drotbohm
3
我不确定谁是踩我的人……我甚至都没有足够的声望来这么做!我不喜欢将评论放在帖子里面的原因是考虑到(不太可能发生的)情况,当我有数千条对于一个帖子的评论时。我想要能够分页显示所有评论而不是每次访问帖子内容时获取整个评论集合。 - ccampo
28
我发布评论的最直观方式是向http://localhost:8080/posts/1/comments发送POST请求。这难道不是最简单和最有意义的方法吗?同时,您仍应该能够拥有专用的评论存储库。这是Spring还是HAL标准不允许这样做? - aycanadal
6
这仍然是推荐/唯一的方法吗?如果子项是非空的(@JoinColumn(nullable=false)),那该怎么办?无法先POST子项,再PUT/PATCH父关联。 - JW Lim
2
有没有使用Spring Data Rest创建的API的指南?我已经谷歌了两个小时,什么都没找到。谢谢! - Skeeve
显示剩余18条评论

48
你需要先发布评论,在发布评论时你可以创建一个关联的帖子实体。它应该看起来像下面这样:

它应该长这个样子:

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

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

它将完美地工作。


2
这对我起作用了。只需确保author.post是可写的(例如通过具有setter或@JsonValue注释)即可。 - scheffield
1
这个功能是否也适用于补丁请求,例如将评论从一篇帖子移动到另一篇帖子? - aycanadal
2
这将是我(大大地)首选的方法,但它似乎对我不起作用。:( 它创建了评论,但没有在解决方案表(POST_COMMENTS)中创建行。有关如何解决的任何建议? - banncee
4
在一个场景中,例如涉及到场馆和地址实体的情况下,如何处理才能确保每个场馆都有对应的地址,且每个地址必须与某个场馆相关联?也就是说,如何避免创建孤立的地址,这些地址可能永远不会被分配给任何东西?也许我错了,但客户端应用程序不应该负责维护数据库中的一致性。我不能指望客户端应用程序创建一个地址并将其分配给一个场馆。是否有一种方法可以在创建实际资源时POST子资源(在本例中为地址实体),以便避免不一致性? - apostrophedottilde
2
我尝试做这个(见此),但由于某种原因只有资源被创建,而关联没有被创建。 - Stefan Falk
显示剩余4条评论

2

有两种类型的映射:关联和组合。在关联的情况下,我们使用连接表的概念,例如:

员工-- 1 到 n -> 部门

所以在关联的情况下将创建3个表: 员工、部门、员工_部门

您只需要在代码中创建EmployeeRepository。此外,映射应该是这样的:

class EmployeeEntity{

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

   }

}

部门实体不包含任何外键的映射...因此,现在当您尝试使用单个JSON请求添加带有部门的员工时,它将被添加....


1

我遇到了同样的情况,因为我使用了一对多映射并通过主实体本身拉取数据,所以我不得不删除子实体的存储库类。现在我正在获得带有数据的完整响应。


1
这件你所谈论的事情可以很容易地通过投影完成。 - kboom

0
对于一对多映射,只需为您要映射的类创建一个POJO,并向其添加@OneToMany注释,它将在内部将其映射到该表ID。
此外,您需要为检索数据的类实现Serializable接口。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接