在Spring Data REST中发布@OneToMany子资源关联

cca*_*mpo 101 java spring spring-data-jpa spring-data-rest spring-boot

目前我有一个使用Spring Data REST的Spring Boot应用程序.我有一个与另一个域实体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...
}
Run Code Online (Sandbox Code Playgroud)

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...
}
Run Code Online (Sandbox Code Playgroud)

他们的Spring Data REST JPA存储库是以下基本实现CrudRepository:

PostRepository.java:

public interface PostRepository extends CrudRepository<Post, Long> { }
Run Code Online (Sandbox Code Playgroud)

CommentRepository.java:

public interface CommentRepository extends CrudRepository<Comment, Long> { }
Run Code Online (Sandbox Code Playgroud)

应用程序入口点是标准的简单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);
    }
}
Run Code Online (Sandbox Code Playgroud)

一切似乎都正常.当我运行应用程序时,一切似乎都正常工作.我可以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"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,当我执行GET时,http://localhost:8080/posts/1/comments我得到一个空对象{}返回,如果我尝试将注释POST到同一个URI,我得到一个HTTP 405方法不允许.

创建Comment资源并将其与此关联的正确方法是什么Posthttp://localhost:8080/comments如果可能的话,我想避免直接POST .

Oli*_*ohm 53

假设您已经发现了post URI并因此发现了关联资源的URI(被认为是$association_uri在下面),它通常采取以下步骤:

  1. 发现管理评论的馆藏资源:

    curl -X GET http://localhost:8080
    
    200 OK
    { _links : {
        comments : { href : "…" },
        posts :  { href : "…" }
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 按照comments链接和POST您的数据到资源:

    curl -X POST -H "Content-Type: application/json" $url 
    { … // your payload // … }
    
    201 Created
    Location: $comment_url
    
    Run Code Online (Sandbox Code Playgroud)
  3. 通过向PUT关联URI 发出a 来将评论分配给帖子.

    curl -X PUT -H "Content-Type: text/uri-list" $association_url
    $comment_url
    
    204 No Content
    
    Run Code Online (Sandbox Code Playgroud)

请注意,在最后一步中,根据规范text/uri-list,您可以提交多个URI,用于标识由换行符分隔的注释,以便一次分配多个注释.

关于一般设计决策的一些注释.阿交/评论例如通常是聚集体,这意味着我会避免从背面参考一个很好的例子CommentPost,并且还避免了CommentRepository完全.如果注释没有自己的生命周期(它们通常不是在组合风格的关系中),你宁可直接内联呈现注释,而是添加和删除注释的整个过程可以通过使用来处理JSON补丁.Spring Data REST 在即将发布的2.2版本的最新候选版本中增加了对该功能的支持.

  • 我发布评论的最直观方式是对http:// localhost:8080/posts/1/comments进行POST.这不是最简单,最有意义的方式吗?与此同时,您仍然可以拥有专门的评论存储库.是春天还是HAL标准不允许这样? (25认同)
  • 对选民失望的选民感兴趣,投票的原因是什么;). (4认同)
  • @OliverGierke这仍然是推荐/唯一的方法吗?如果孩子不可空(@JoinColumn(nullable = false)`)该怎么办?首先POST子项,然后PUT/PATCH父关联是不可能的. (4认同)
  • 我不确定失败的选民......我甚至没有名声这么做!我不一定喜欢将评论与帖子结合在一起的原因是因为当我对一篇帖子有数千条评论时,考虑(不太可能)的情况.我希望能够对评论集合进行分页,而不是每次我想访问帖子的内容时都获取整个评论集合. (3认同)
  • 是否有使用Spring Data Rest创建的api的指南?我用谷歌搜索了2个小时,什么也没找到。谢谢! (2认同)
  • @OliverGierke:在第 2 步的情况下,如果您的评论对 post_id 有非空约束,这可以工作吗?您将如何避免这样的 ConstraintViolationExceptions:错误:post_id 列中的空值违反了非空约束:无法执行语句”} (2认同)

小智 48

您必须先发布评论,在发布评论时,您可以创建一个关联发布实体.

它应该如下所示:

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

{"author":"abc","content":"PQROHSFHFSHOFSHOSF", "post":"http://{server:port}/post/1"}
Run Code Online (Sandbox Code Playgroud)

它会完美地运作.

  • 这对我有用。只需确保author.post是可写的即可(例如,使用setter或@JsonValue注解) (2认同)
  • 这将是我(非常)首选的方法,但它似乎对我不起作用。:( 它会创建评论,但不会在解析表 (POST_COMMENTS) 中创建行。有关如何解决的任何建议? (2认同)
  • 对于场景(例如,场所和地址实体),场所必须具有地址且地址必须与场所相关联的情形,该方法将是什么?我的意思是...为了避免创建一个可能永远不会分配给任何东西的孤立地址?也许我说错了,但是客户端应用程序绝不应该负责维护数据库内的一致性。我不能依靠客户端应用程序创建地址,然后确定分配给场地。有没有一种方法可以通过创建实际资源来发布子资源(在这种情况下为Address实体),以便避免不一致? (2认同)
  • 我尝试这样做([见这里](/sf/ask/3316363431/))但出于某种原因,只有资源,而不是关联,被建造。 (2认同)