如何与Spring Data REST和JPA保持双向关系? [英] How to maintain bi-directional relationships with Spring Data REST and JPA?

查看:161
本文介绍了如何与Spring Data REST和JPA保持双向关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Spring Data REST。如果您具有oneToMany或ManyToOne关系,则PUT操作在非拥有实体上返回200,但实际上不会保留已连接的资源。

Working with Spring Data REST. IF you have a oneToMany or ManyToOne relationship, the PUT operation returns 200 on the "non-owning" entity but does not actually persist the joined resource.

示例实体。

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

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

    String fullName

    @ManyToMany(mappedBy = 'authors')
    Set<BookEntity> books
}


@Entity(name = 'book')
@EqualsAndHashCode
class BookEntity implements Book {

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

    @Column(nullable = false)
    String title

    @Column(nullable = false)
    String isbn

    @Column(nullable = false)
    String publisher

    @ManyToMany(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
    Set<AuthorEntity> authors
}

如果你用 PagingAndSortingRepository支持它们,你可以获取一本书,按照书上的作者链接进行PUT,并与作者的URI进行关联。你不能走另一条路。

If you back them with a PagingAndSortingRepository, you can GET a Book, follow the authors link on the book and do a PUT with the URI of a author to associate with. You cannot go the other way.

如果你对作者进行GET并在它的书籍链接上做一个PUT,则响应返回200,但这种关系永远不会持续。

If you do a GET on an Author and do a PUT on it's books link, the response returns 200, but the relationship is never persisted.

这是预期的行为吗?

推荐答案

tl; dr



关键在于Spring Data REST中没有任何东西 - 因为你可以轻松地让它在你的场景中运行 - 但要确保你的模型保持两端同步中的关联。

tl;dr

The key to that is not so much anything in Spring Data REST - as you can easily get it to work in your scenario - but making sure that your model keeps both ends of the association in sync.

你在这里看到的问题源于Spring Data REST基本上修改 AuthorEntity books 属性。这本身并不反映 BookEntity authors 属性中的此更新。这必须手动解决,这不是Spring Data REST组成的约束,而是JPA的一般工作方式。您只需手动调用setter并尝试保留结果即可重现错误行为。

The problem you see here arises from the fact that Spring Data REST basically modifies the books property of your AuthorEntity. That itself doesn't reflect this update in the authors property of the BookEntity. This has to be worked around manually, which is not a constraint that Spring Data REST makes up but the way that JPA works in general. You will be able to reproduce the erroneous behavior by simply invoking setters manually and trying to persist the result.

如果删除双向关联不是一个选项(请参阅下面我为什么建议这样做),唯一的方法是确保关联的更改反映在双方。通常人们通过在添加图书时手动将作者添加到 BookEntity 来处理这个问题:

If removing the bi-directional association is not an option (see below on why I'd recommend this) the only way to make this work is to make sure changes to the association are reflected on both sides. Usually people take care of this by manually adding the author to the BookEntity when a book is added:

class AuthorEntity {

  void add(BookEntity book) {

    this.books.add(book);

    if (!book.getAuthors().contains(this)) {
       book.add(this);
    }
  }
}

额外的if子句''如果你想确保来自另一方的更改也被传播,那么也可以添加到 BookEntity 一侧。基本上需要 if ,否则两种方法会不断调用自己。

The additional if clause would've to be added on the BookEntity side as well if you want to make sure that changes from the other side are propagated, too. The if is basically required as otherwise the two methods would constantly call themselves.

Spring Data REST默认使用字段访问,以便实际上没有方法可以将此逻辑放入。一种选择是切换到属性访问并将逻辑放入setter。另一种选择是使用一个用 @PreUpdate / @PrePersist 注释的方法迭代实体并确保修改反映在双方。

Spring Data REST, by default uses field access so that theres actually no method that you can put this logic into. One option would be to switch to property access and put the logic into the setters. Another option is to use a method annotated with @PreUpdate/@PrePersist that iterates over the entities and makes sure the modifications are reflected on both sides.

如你所见,这增加了域模型非常复杂。我昨天在Twitter上开玩笑说:

As you can see, this adds quite a lot of complexity to the domain model. As I joked on Twitter yesterday:


#1双向关联规则:不要使用它们......:)

#1 rule of bi-directional associations: don't use them… :)

如果您尽可能不尝试使用双向关系,而是回到存储库以获取所有制作的实体,它通常会简化问题在协会的背后。

It usually simplifies the matter if you try not to use bi-directional relationship whenever possible and rather fall back to a repository to obtain all the entities that make up the backside of the association.

确定哪一方需要削减的一个良好的启发式方法是考虑关联的哪一方真正是核心,对你所在的领域至关重要重新建模。在你的情况下,我认为对于一个没有她写的书而存在的作者来说完全没问题。另一方面,没有作者的书根本没有太多意义。所以我会在 BookEntity 中保留 authors 属性,但在 BookRepository上引入以下方法

A good heuristics to determine which side to cut is to think about which side of the association is really core and crucial to the domain you're modeling. In your case I'd argue that it's perfectly fine for an author to exist with no books written by her. On the flip side, a book without an author doesn't make too much sense at all. So I'd keep the authors property in BookEntity but introduce the following method on the BookRepository:

interface BookRepository extends Repository<Book, Long> {

  List<Book> findByAuthor(Author author);
}

是的,这需要以前可以调用的所有客户 author.getBooks()现在使用存储库。但从积极的方面来说,你已经删除了域对象中的所有内容,并在整个过程中创建了从书到作者的清晰依赖方向。书籍取决于作者,但不是相反。

Yes, that requires all clients that previously could just have invoked author.getBooks() to now work with a repository. But on the positive side you've removed all the cruft from your domain objects and created a clear dependency direction from book to author along the way. Books depend on authors but not the other way round.

这篇关于如何与Spring Data REST和JPA保持双向关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆