[英]Hibernate Many-to-Many with join-class Cascading issue
I have a Many-to-Many
relationship between the class Foo
and Bar
. 我在
Foo
和Bar
之间有Many-to-Many
关系。 Because I want to have additional information on the helper table, I had to make a helper class FooBar
as explained here: The best way to map a many-to-many association with extra columns when using JPA and Hibernate 因为我想获得有关辅助表的其他信息,所以我必须按照此处的说明创建一个辅助类
FooBar
: 使用JPA和Hibernate时,使用额外列映射多对多关联的最佳方法
I created a Foo, and created some bars (saved to DB). 我创建了一个Foo,并创建了一些条形图(保存到DB)。 When I then add one of the bars to the foo using
当我然后使用其中一个条添加到foo
foo.addBar(bar); // adds it bidirectionally
barRepository.save(bar); // JpaRepository
then the DB-entry for FooBar is created - as expected. 然后创建FooBar的DB条目 - 正如预期的那样。
But when I want to remove that same bar again from the foo, using 但是,当我想再次从foo中删除相同的栏时,使用
foo.removeBar(bar); // removes it bidirectionally
barRepository.save(bar); // JpaRepository
then the earlier created FooBar-entry is NOT deleted from the DB. 然后, 不会从数据库中删除先前创建的FooBar条目。 With debugging I saw that the
foo.removeBar(bar);
通过调试我看到了
foo.removeBar(bar);
did indeed remove bidirectionally. 确实删除了双向。 No Exceptions are thrown.
没有异常被抛出。
Am I doing something wrong? 难道我做错了什么? I am quite sure it has to do with Cascading options, since I only save the bar.
我很确定它与Cascading选项有关,因为我只保存吧。
What I have tried: 我尝试过的:
adding orphanRemoval = true
on both @OneToMany - annotations, which did not work. 在@OneToMany上添加
orphanRemoval = true
- 注释,这些都不起作用。 And I think that's correct, because I don't delete neither Foo nor Bar, just their relation. 我认为这是正确的,因为我不删除 Foo和Bar,只删除他们的关系。
excluding CascadeType.REMOVE from the @OneToMany annotations, but same as orphanRemoval I think this is not for this case. 从@OneToMany注释中排除CascadeType.REMOVE,但与orphanRemoval相同我认为这不适用于这种情况。
Edit: I suspect there has to be something in my code or model that messes with my orphanRemoval, since there are now already 2 answers who say that it works (with orphanRemoval=true
). 编辑:我怀疑在我的代码或模型中必须有一些与我的孤儿电影混淆的东西,因为现在已经有2个答案说它有效(使用
orphanRemoval=true
)。
The original question has been answered, but if anybody knows what could cause my orphanRemoval not to work I would really appreciate your input. 最初的问题已得到解答,但如果有人知道什么可能导致我的孤儿无法工作,我会非常感谢您的意见。 Thanks
谢谢
Code: Foo, Bar, FooBar 代码:Foo,Bar,FooBar
public class Foo {
private Collection<FooBar> fooBars = new HashSet<>();
// constructor omitted for brevity
@OneToMany(cascade = CascadeType.ALL, mappedBy = "foo", fetch = FetchType.EAGER)
public Collection<FooBar> getFooBars() {
return fooBars;
}
public void setFooBars(Collection<FooBar> fooBars) {
this.fooBars = fooBars;
}
// use this to maintain bidirectional integrity
public void addBar(Bar bar) {
FooBar fooBar = new FooBar(bar, this);
fooBars.add(fooBar);
bar.getFooBars().add(fooBar);
}
// use this to maintain bidirectional integrity
public void removeBar(Bar bar){
// I do not want to disclose the code for findFooBarFor(). It works 100%, and is not reloading data from DB
FooBar fooBar = findFooBarFor(bar, this);
fooBars.remove(fooBar);
bar.getFooBars().remove(fooBar);
}
}
public class Bar {
private Collection<FooBar> fooBars = new HashSet<>();
// constructor omitted for brevity
@OneToMany(fetch = FetchType.EAGER, mappedBy = "bar", cascade = CascadeType.ALL)
public Collection<FooBar> getFooBars() {
return fooBars;
}
public void setFooBars(Collection<FooBar> fooBars) {
this.fooBars = fooBars;
}
}
public class FooBar {
private FooBarId id; // embeddable class with foo and bar (only ids)
private Foo foo;
private Bar bar;
// this is why I had to use this helper class (FooBar),
// else I could have made a direct @ManyToMany between Foo and Bar
private Double additionalInformation;
public FooBar(Foo foo, Bar bar){
this.foo = foo;
this.bar = bar;
this.additionalInformation = .... // not important
this.id = new FooBarId(foo.getId(), bar.getId());
}
@EmbeddedId
public FooBarId getId(){
return id;
}
public void setId(FooBarId id){
this.id = id;
}
@ManyToOne
@MapsId("foo")
@JoinColumn(name = "fooid", referencedColumnName = "id")
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
@ManyToOne
@MapsId("bar")
@JoinColumn(name = "barid", referencedColumnName = "id")
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
// getter, setter for additionalInformation omitted for brevity
}
I tried this out from the example code. 我从示例代码中尝试了这个。 With a couple of 'sketchings in' this reproduced the fault.
通过几个'草图',这再现了错误。
The resolution did turn out to be as simple as adding the orphanRemoval = true
you mentioned though. 事实证明,解决方案就像添加你提到的
orphanRemoval = true
一样简单。 On Foo.getFooBars()
: 在
Foo.getFooBars()
:
@OneToMany(cascade = CascadeType.ALL, mappedBy = "foo", fetch = FetchType.EAGER, orphanRemoval = true)
public Collection<FooBar> getFooBars() {
return fooBars;
}
It seemed easiest to post that reproduction up to GitHub - hopefully there's a further subtle difference or something I missed in there. 将这种复制发布到GitHub似乎最容易 - 希望还有一个微妙的差异或我错过的东西。
This is based around Spring Boot and an H2 in-memory database so should work with no other environment - just try mvn clean test
if in doubt. 这是基于Spring Boot和H2内存数据库所以应该没有其他环境 - 如果有疑问,只需尝试
mvn clean test
。
The FooRepositoryTest
class has the test case. FooRepositoryTest
类有测试用例。 It has a verify for the removal of the linking FooBar
, or it may just be easier to read the SQL that gets logged. 它有一个验证删除链接
FooBar
,或者它可能只是更容易读取记录的SQL。
Edit 编辑
This is the screenshot mentioned in a comment below: 这是下面评论中提到的屏幕截图:
I've tested your scenario and did the following three modifications to make it work: 我已经测试了你的场景,并进行了以下三个修改以使其工作:
Java Persistence 2.1.
Java Persistence 2.1。 Chapter 3.2.3
第3.2.3节
Operation remove
操作删除
• If X is a new entity, it is ignored by the remove operation .
•如果X是新实体,则删除操作将忽略它 。 However, the remove operation is cascaded to entities referenced by X, if the relationship from X to these other entities is annotated with the cascade=REMOVE or cascade=ALL annotation element value.
但是,如果从X到这些其他实体的关系使用cascade = REMOVE或cascade = ALL annotation元素值进行注释,则删除操作会级联到由X引用的实体。
• If X is a managed entity, the remove operation causes it to become removed.
•如果X是托管实体,则删除操作会导致其被删除。 The remove operation is cascaded to entities referenced by X, if the relationships from X to these other entities is annotated with the cascade=REMOVE or cascade=ALL annotation element value.
如果从X到这些其他实体的关系使用cascade = REMOVE或cascade = ALL注释元素值进行注释,则删除操作将级联到由X引用的实体。
Check that you already use operation persist
for you Entities Foo
(or FooBar
or Bar
). 检查您是否已经为实体
Foo
(或FooBar
或Bar
)使用操作persist
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.