如何在 Spring Data Rest 中的两个实体之间发布和 PUT 关系@OneToMany / @ManyToOne?

Posted

技术标签:

【中文标题】如何在 Spring Data Rest 中的两个实体之间发布和 PUT 关系@OneToMany / @ManyToOne?【英文标题】:How to POST and PUT relations @OneToMany / @ManyToOne between two entities in Spring Data Rest? 【发布时间】:2016-09-26 00:56:22 【问题描述】:

我有一个用于 Spring Data Rest 实现的简单 Spring Boot 应用程序。

这是主类:

@SpringBootApplication
public class Application 

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

我有两个简单的实体:书和作者。 彼此的关系是 1 Author -> N Books

这是 Author.class:

@Entity
@Table
public class Author 

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(columnDefinition = "BINARY(16)", length = 16)
    private UUID id;

    @Column
    private String name;

    @OneToMany(fetch = FetchType.LAZY, targetEntity = Book.class, mappedBy = "author")
    private List<Book> books;

    // getters and setters


这是 Book.class:

@Entity
@Table
public class Book 

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(columnDefinition = "BINARY(16)", length = 16)
    private UUID id;

    @Column
    private String title;

    @Column
    private String language;

    @ManyToOne(fetch = FetchType.EAGER, targetEntity = Author.class)
    private Author author;

    // getters and setters


这是“AuthorRepository”:

@RestResource(path = "authors", rel = "authors")
public interface AuthorRepository extends JpaRepository<Author, UUID> 

这是“BookRepository”:

@RestResource(path = "books", rel = "books")
public interface BookRepository extends JpaRepository<Book, UUID> 

应用程序运行完美,在 url http://localhost:8080/ 我有这个响应页面:


  "_links" : 
    "authors" : 
      "href" : "http://localhost:8080/authors?page,size,sort",
      "templated" : true
    ,
    "books" : 
      "href" : "http://localhost:8080/books?page,size,sort",
      "templated" : true
    ,
    "profile" : 
      "href" : "http://localhost:8080/profile"
    
  

网址http://localhost:8080/authors返回此页面:


  "_embedded" : 
    "authors" : [ ]
  ,
  "_links" : 
    "self" : 
      "href" : "http://localhost:8080/authors"
    ,
    "profile" : 
      "href" : "http://localhost:8080/profile/authors"
    
  ,
  "page" : 
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  

而 url http://localhost:8080/books 返回此页面:


  "_embedded" : 
    "books" : [ ]
  ,
  "_links" : 
    "self" : 
      "href" : "http://localhost:8080/books"
    ,
    "profile" : 
      "href" : "http://localhost:8080/profile/books"
    
  ,
  "page" : 
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  

我正在尝试从 Book 类开始进行一些 HTTP POST。

HTTP POST
url: http://localhost:8080/books
header: Content-Type:application/json
payload:  "title": "Book Title" 

Status: 201: Created
Location: http://localhost:8080/books/61311c9b-b33a-463c-9e6e-8e5efc0a7ad1

其实就是urlhttp://localhost:8080/books/61311c9b-b33a-463c-9e6e-8e5efc0a7ad1返回这个页面:


  "title" : "Book Title",
  "language" : null,
  "_links" : 
    "self" : 
      "href" : "http://localhost:8080/books/61311c9b-b33a-463c-9e6e-8e5efc0a7ad1"
    ,
    "book" : 
      "href" : "http://localhost:8080/books/61311c9b-b33a-463c-9e6e-8e5efc0a7ad1"
    ,
    "author" : 
      "href" : "http://localhost:8080/books/61311c9b-b33a-463c-9e6e-8e5efc0a7ad1/author"
    
  

我为作者做了同样的事情。

HTTP POST
url: http://localhost:8080/authors
header: Content-Type:application/json
payload:  "name": "Author Name" 

Status: 201: Created
Location: http://localhost:8080/authors/634d3bd8-abe6-472b-97cd-04a455bdfb11

这是该网址的响应页面:


  "name" : "Author Name",
  "_links" : 
    "self" : 
      "href" : "http://localhost:8080/authors/634d3bd8-abe6-472b-97cd-04a455bdfb11"
    ,
    "author" : 
      "href" : "http://localhost:8080/authors/634d3bd8-abe6-472b-97cd-04a455bdfb11"
    ,
    "books" : 
      "href" : "http://localhost:8080/authors/634d3bd8-abe6-472b-97cd-04a455bdfb11/books"
    
  

注意到作者和书之间的关系还不存在,所以我尝试发送一个 PUT 请求。

HTTP PUT
url: http://localhost:8080/authors/634d3bd8-abe6-472b-97cd-04a455bdfb11
header: Content-Type:application/json
payload:  "books": ["http://localhost:8080/books/61311c9b-b33a-463c-9e6e-8e5efc0a7ad1"] 

Status: 204: No Content
Location: http://localhost:8080/authors/634d3bd8-abe6-472b-97cd-04a455bdfb11

我有一个 HTTP 204(无内容)作为响应代码。 如果我转到 url http://localhost:8080/authors/634d3bd8-abe6-472b-97cd-04a455bdfb11/books 我会期望这本书是结果,但我有这个结果:


  "_embedded" : 
    "books" : [ ]
  ,
  "_links" : 
    "self" : 
      "href" : "http://localhost:8080/authors/634d3bd8-abe6-472b-97cd-04a455bdfb11/books"
    
  

“books”属性仍为空。为什么?

此外,此 URL 返回一个空页面:http://localhost:8080/books/61311c9b-b33a-463c-9e6e-8e5efc0a7ad1/author

没有像我预期的那样处理关系。

如果我在插入书之前从作者开始执行相同的过程,则关系存在。

如何从 Book 实体开始保存两个实体之间的关系?

谢谢

【问题讨论】:

面临同样的问题,这里描述***.com/questions/39229472/… 【参考方案1】:

要使这种情况反向发生,您的 @OneToMany(fetch = FetchType.LAZY, targetEntity = Book.class, mappedBy = "author") 应该在其中包含级联选项,以实际持久化图书实体中的更改。

试试这个:

@OneToMany(fetch = FetchType.LAZY, targetEntity = Book.class, mappedBy = "author", cascade = CascadeType.ALL)

【讨论】:

【参考方案2】:

您正在使用双向 OneToMany,因此您必须使用“实用程序方法”来在添加或删除子元素时同步两端(请参阅manual)。

但是你几乎可以做同样的事情,只是修改books setter,像这样:

@Entity
public class Author 

    //...

    @OneToMany(mappedBy = "author")
    private List<Book> books;

    public void setBooks(List<Book> books) 
        books.forEach(book -> book.setAuthor(this));
        this.books = books;
    


    //...

见我的example。

附:如果您的实体是独立的,请不要使用cascade = CascadeType.ALL,而是使用cascade = CascadeType.PERSIST, CascadeType.MERGE(或者根本不使用级联)。

【讨论】:

以上是关于如何在 Spring Data Rest 中的两个实体之间发布和 PUT 关系@OneToMany / @ManyToOne?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring-Data-Rest 中实现细粒度的访问控制?

在 Spring Data rest json Response 中动态过滤实体字段

如何让Spring的Data Rest Repository按名称而不是id来检索数据

处理事务中的 spring-data-rest 应用程序事件

如何将 spring-data-rest 与 spring websocket 混合到一个实现中

Spring Data Rest 中同一实体的多个存储库