【问题标题】:How to create resource with unique many-to-many relationsships in Spring Data REST?如何在 Spring Data REST 中创建具有独特多对多关系的资源?
【发布时间】:2015-05-02 01:45:48
【问题描述】:

我正在使用 Spring Boot 并尝试使用 Spring Data REST 在实体之间实现多对多关系,其中不会创建依赖 Author 实体的重复项。

给定以下代码,当我发布带有作者的初始书时,创建了作者,创建了书,并创建了关系(查找表条目)。当我以同一作者发布第二本书时,会创建第二本书并尝试创建第二作者,但由于作者姓名的唯一性限制而失败。

我希望/希望发生的是,创建了第二本书,并创建了一个将第二本书与原始作者记录相关联的条目,而不是试图创建第二个/重复作者。

@Entity(name = 'book')
class Book {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    long id
    ...
    @ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.ALL])
    @JoinTable(name = 'books_authors', joinColumns = @JoinColumn(name = 'book_id'),
        inverseJoinColumns = @JoinColumn(name = 'author_id'))
    @RestResource(exported = false)
    Set<Author> authors
}

@Entity(name = 'author')
@ToString
class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    long id

    @Column(nullable = false, unique = true)
    String fullName

    @ManyToMany(mappedBy = "authors")
    Set<Book> books
}

@RepositoryRestResource(collectionResourceRel = "authors", path = "authors")
interface AuthorRepository extends PagingAndSortingRepository<Author, Long> { }

@RepositoryRestResource(collectionResourceRel = "books", path = "books")
interface BookRepository extends PagingAndSortingRepository<Book, Long> { }

$ curl -i -X POST -H "Content-Type:application/json" -d '{ "title" : "Book One",  "authors" { "fullName": "AuthorName"} }' ....
HTTP/1.1 201 Created

$ curl -i -X POST -H "Content-Type:application/json" -d '{ "title" : "Book Two",  "authors" { "fullName": "AuthorName"} }' ....

第二个帖子由于尝试使用重复的fullName 创建新作者而失败。

我已经使用 Spring Data 实现了相同的关系,但没有使用 Spring Data REST。在其中,我有控制器和服务,并且能够在 createBook(Book book) 方法中覆盖它。但是使用 Spring Data REST,我没有要实现的服务。我尝试在AbstractRepositoryEventListener&lt;Book&gt;#onBeforeCreate() 中实现相同的逻辑,但最终出现了分离实体问题。

这似乎是我应该能够仅使用 JPA 关系定义来处理的事情,但我似乎无法做到。

【问题讨论】:

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


    【解决方案1】:

    您对 REST 和 Spring Data REST 所基于的一些概念略有误解或误用。让我从解释您提供的代码开始。

    基本原理

    由于Author 和Book 都由存储库管理,它们基本上从普通实体提升到聚合根。 DDD 中的聚合根应该管理自身内部的不变量,并且不能作为操纵另一个聚合的副作用进行调整。这就是为什么 Spring Data REST 默认为它们公开专用资源并在两者之间创建链接(字面意思)。

    我想简要阐述的另一个方面是 REST 中的身份主题。它实际上非常简单,因为它非常明确。资源有身份——它们的 URI(顾名思义)。因此,服务器必须识别身份的唯一方法是向其提供相同的 URI。

    您的样品

    因此,在您的特定示例中,服务器无法判断您发布的第二个Book 指的是同一位作者。基本上是因为您没有引用,而是内联,这使得 Author 成为 Book 聚合的一部分,这与您首先为两者都拥有存储库的方法相矛盾。

    如果你仔细想想,你的例子提出了两个甚至使事情复杂化的问题:

    1. 为什么要第二次转移Author 的所有属性?您基本上想表达的是:“这本书属于我已经创建的作者”,您可以通过将创建的第一作者的 URI 作为第二个 Book 的 author 属性的值提交给被创建。

    2. 如果第二个内联的Author 文档也更改了某些属性,会发生什么?这基本上与我首先概述的聚合假设相矛盾,并回到问题 1:您想引用已经存在的内容,而不是重新提交内容。

    建议步骤

    所以我基本上建议如下:

    1. 从Book 的authors 属性中删除@RestResource(exported = false)。
    2. 提交您已有的第一个请求。
    3. 点击服务器返回的Location 标头中提供的链接。
    4. 点击链接目标中提供的authors 链接。
    5. 使用该资源返回的链接并在随后的创建请求中使用它们来填充authors 属性。

    如果您可以在单独的步骤中创建作者,我什至建议您这样做,因为它们会立即返回创建的作者的 URI,以便它们可以在随后的书籍创建中使用。

    【讨论】:

      猜你喜欢
      • 2019-01-29
      • 2015-11-10
      • 1970-01-01
      • 2014-03-18
      • 2016-11-29
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 1970-01-01
      相关资源
      最近更新 更多