在 JPA 中的两个相关实体中出现一些急切的 n 到 n 关系中进行更新
更改 JPA 中两个实体之间的 n 对 n 关系的最佳方法是什么?
让我用一个例子来解释:假设我有一个 Author
和一个 Book
实体,其中 Book
可以有不同的 Author< /code> 和
作者
可以在各种书籍
上书写。该模型表示为 n 到 n 的热切关系:
@Entity
public class Author {
@ManyToMany(fetch=FetchType.EAGER)
private List<Book> books;
// ...
}
另外:
@Entity
public class Book {
@ManyToMany(fetch=FetchType.EAGER)
private List<Author> authors;
// ...
}
我想在这些实体中执行一系列操作 - 例如,将 Author
标记为 Book 的作者
:
public class SomeService {
public void addAuthor(Book book, Author author) {
book.getAuthors().add(author);
em.persist(book);
}
}
或从作者
的书籍中删除书籍
:
public class SomeOtherService {
public void removeBook(Author author, Book book) {
author.getBooks().remove(book);
em.persist(author);
}
}
但是,我希望在不更新的实体中注册更新。当我调用 addAuthor()
时,我希望更新的 Book
也出现在 Author
的图书列表中。当我调用 removeBook()
时,我希望将 Author
从 Book
的作者列表中删除。
如果代码如上面的那样,则不会发生。我当前的解决方案是在两个实体中执行相应的操作,或多或少如下所示:
public class SomeService {
public void addAuthor(Book book, Author author) {
book.getAuthors().add(author);
author.getBooks().add(book);
em.persist(book);
em.persist(author);
}
}
不过,对我来说,这看起来是一个蹩脚的解决方案。所以,我的问题是:有更好的方法吗?
提前致谢?
What is the best way to change n-to-n relationships between two entities in JPA.
Let me explain with an example: suppose I have both an Author
and a Book
entity, where a Book
can have various Author
s and an Author
can write on various Book
s. This model is represented as a n-to-n eager relationship:
@Entity
public class Author {
@ManyToMany(fetch=FetchType.EAGER)
private List<Book> books;
// ...
}
Also:
@Entity
public class Book {
@ManyToMany(fetch=FetchType.EAGER)
private List<Author> authors;
// ...
}
I want to execute a series of operations in those entities - for example, mark an Author
as the author of a Book
:
public class SomeService {
public void addAuthor(Book book, Author author) {
book.getAuthors().add(author);
em.persist(book);
}
}
or remove a Book
from the books of an Author
:
public class SomeOtherService {
public void removeBook(Author author, Book book) {
author.getBooks().remove(book);
em.persist(author);
}
}
However, I would like to have the update registered in the entity not being updated too. When I call addAuthor()
, I expect the updated Book
to be also present in the list of books of the Author
. I want the Author
to be removed of the Book
's list of authors when I call removeBook()
.
If the code is such as the above ones it does not happen. My current solution is to execute de corresponding operation in both entities, more or less like shown below:
public class SomeService {
public void addAuthor(Book book, Author author) {
book.getAuthors().add(author);
author.getBooks().add(book);
em.persist(book);
em.persist(author);
}
}
It looks like a lame solution to me, though. So, my question is: is there a better way to do it?
Thanks in advance?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
对我来说这并不是一个蹩脚的解决方案,因为它对正在发生的事情非常明确,一个优点是你有一个非常明确的代码。但是,如果这对您来说过于明确,那么您有多种选择。
正如 Jay 建议的那样,您可以编写一个 @PreUpdate 回调方法来执行以下操作:
为此,您必须使用 @ManyToMany 注释的级联属性将更改级联到作者
,或者您可以增加 CascadeType 的粒度。
还有一种替代方法是通过委托方法处理集合。
这样做的优点是您可以完全控制 addAuthor 方法,但对于使用您的实体的人来说不太直观。
它可以写成:
然后您需要实现 addBook。以及级联变化。
你的选择也不错。您可以使用辅助类来实现此目的,但这会增加复杂性,因此请记住这一点。
It's not really a lame solution to me as it is very explicit on what's going on, an advantage is that you have a very explicit code. However, if to you that is excessively explicit then you have several alternatives.
As Jay suggested you could write a @PreUpdate callback method that does the:
for this to work however you have to cascade changes to the authors using the cascade attribute of the @ManyToMany annotation
for instance, or you can increase the granularity of the CascadeType.
There's an alternative that is handling the collections through delegate methods.
This has the advantage that you have a total control over the addAuthor method but is less intuitive to people using your entity.
It could be written as:
you would then need to implement addBook. And cascade changes.
Your option isn't bad either. You can use helper classes to achieve this but this increases complexity so bare that in mind.
维持双向关系状态可能具有挑战性。最后两者都
需要被调用。
在我工作的地方,我们使用aspectJ和一些内部元数据来处理关联双方的设置。对于小型项目来说,这有点矫枉过正,但你这样做的方式很好。
Maintaining bidirectional relation state can be challenging. In the end both
need to be called.
Where I work we use aspectJ with some internal metadata to handle the setting both sides of the association. For small project this is a bit overkill, and the way you do it is fine.
Vincent Partington 曾经写过一篇关于双向关联 我相信你会觉得有趣。本质上,他建议在关系中的每个伙伴上使用两种关系添加方法:一种是内部(可能是包范围的)方法,它只是将一个项目添加到集合中,另一种是外部方法,它调用两者的内部方法 合作伙伴。相同的模式可以应用于去除方法。
Vincent Partington once wrote a blog post about bidirectional assocations which i believe you will find interesting. Essentially, he suggests having two relationship-adding methods on each partner in the relationship: an inner (package-scoped, presumably) one which simply adds an item to the collection, and an outer one which calls the inner methods on both partners. The same pattern can be applied to removal methods.
看看@PreXXXX & @PostXXXX 注释。根据您使用的数据库,您最好编写自己的触发器。
如果您正在寻找最不可知的方法,那么您提出的解决方案应该可以解决问题。
编辑:
另一种方法是保存触发器代码并在 CRUD 之后将其作为本机 SQL 运行(假设您在测试中创建并删除)。
Look into @PreXXXX & @PostXXXX annotations. Depending on the DB you're using, you might be better off going in and writing your own triggers.
If you're looking for the most agnostic approach, the solution you put down should do the trick.
Edit:
Other approach is to save your trigger code and run it as native SQL after the CRUD (assuming you create & drop in your tests).