【问题标题】:POSTing a @OneToMany sub-resource association in Spring Data REST在 Spring Data REST 中发布 @OneToMany 子资源关联
【发布时间】:2014-10-08 08:04:56
【问题描述】:

目前我有一个使用 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...
}

评论.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);
    }
}

一切似乎都正常工作。当我运行该应用程序时,一切似乎都正常工作。我可以像这样向http://localhost:8080/posts 发布一个新的 Post 对象:

身体: {"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"
        }
    }
}

但是,当我在 http://localhost:8080/posts/1/comments 执行 GET 时,我得到一个空对象 {} 返回,如果我尝试向同一个 URI 发布评论,我得到一个 HTTP 405 Method Not Allowed。

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

【问题讨论】:

标签: java spring spring-boot spring-data-jpa spring-data-rest


【解决方案1】:

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

  1. 发现管理 cmets 的集合资源:

     curl -X GET http://localhost:8080
    
     200 OK
     { _links : {
         comments : { href : "…" },
         posts :  { href : "…" }
       }
     }
    
  2. 按照comments 链接和POST 您的数据访问资源:

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

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

注意,最后一步,根据text/uri-list的规范,你可以提交多个标识cmets的URI,用换行符分隔,一次分配多个cmets。

关于一般设计决策的更多说明。 post/cmets 示例通常是聚合的一个很好的示例,这意味着我会避免从CommentPost 的反向引用,同时也完全避免CommentRepository。如果 cmets 没有自己的生命周期(它们通常没有组合式关系),您宁愿让 cmets 直接内联渲染,而添加和删除 cmets 的整个过程可以通过使用来处理JSON Patch。 Spring Data REST 在即将发布的 2.2 版的最新候选版本中添加了support for that

【讨论】:

  • 对这里的投票者感兴趣,投票的原因是什么;)。
  • 我不确定选民是否反对......我什至没有这样做的声誉!我不一定喜欢将 cmets 与帖子内联的原因是因为考虑(不太可能)当我有数千个 cmets 用于单个帖子时的情况。我希望能够对 cme​​ts 的集合进行分页,而不是每次我想访问帖子的内容时都获取所有的 cmets。
  • 我发表评论的最直观方式是向localhost:8080/posts/1/comments 发帖。这不是最简单最有意义的方式吗?同时,您仍然应该能够拥有一个专门的评论存储库。是 Spring 还是 HAL 标准不允许这样做?
  • @OliverGierke 这仍然是推荐/唯一的方法吗?如果孩子不可为空(@JoinColumn(nullable=false))怎么办?不可能先发布孩子,然后 PUT/PATCH 父关联。
  • 有没有使用spring data rest创建的api的指南?我已经用谷歌搜索了 2 个小时,但一无所获。谢谢!
【解决方案2】:

您必须先发表评论,并在发表评论时创建关联帖子实体。

它应该如下所示:

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

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

它会工作得很好。

【讨论】:

  • 这对我有用。只需确保 author.post 是可写的(例如通过设置器或 @JsonValue 注释)
  • 这是否也适用于补丁请求,例如将评论从一个帖子移动到另一个帖子?
  • 这将是我(非常)喜欢的方法,但它似乎对我不起作用。 :( 它会创建评论,但不会在解析表 (POST_COMMENTS) 中创建行。关于如何解析的任何建议?
  • 对于一个场景,例如场地和地址实体,场地必须有一个地址并且地址必须与场地相关联,该方法是什么?我的意思是...避免创建一个可能永远不会分配给任何东西的孤立地址?也许我错了,但客户端应用程序永远不应该负责维护数据库内的一致性。我不能依赖客户端应用程序创建地址然后明确分配给地点。有没有办法在创建实际资源时发布子资源(在这种情况下是地址实体),这样我就可以避免不一致??
  • 我尝试这样做 (see here),但由于某种原因,只创建了资源,而不是关联。
【解决方案3】:

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

员工--1 到 n-> 部门

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

您只需要在代码中创建 EmployeeRepository。除了那个映射应该是这样的:

class EmployeeEntity{

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

   }

}

Depatment 实体将不包含任何外键映射...所以现在当您尝试在单个 json 请求中添加 Employee 和 Department 的 POST 请求时,它将被添加...。

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 你说的这件事,用投影可以轻松搞定
    【解决方案5】:

    对于oneToMany映射,只需为要映射的类创建一个POJO,并对其进行@OneToMany注释,然后在内部将其映射到该表id。

    此外,您需要为要检索数据的类实现 Serializable 接口。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-04
      • 2017-01-18
      • 2017-04-22
      • 1970-01-01
      • 1970-01-01
      • 2015-07-06
      • 2014-01-18
      • 2015-04-25
      相关资源
      最近更新 更多