我在类Many-to-Many
和Foo
之间有一种Bar
关系。因为我想在帮助程序表上获得更多信息,所以我必须制作一个帮助程序类FooBar
,如此处所述:The best way to map a many-to-many association with extra columns when using JPA and Hibernate
我创建了一个Foo,并创建了一些酒吧(保存到DB)。然后当我使用
将其中一根杠添加到foo中时foo.addBar(bar); // adds it bidirectionally
barRepository.save(bar); // JpaRepository
然后按预期创建FooBar的数据库条目。
但是当我想再次从foo中删除同一条时,使用
foo.removeBar(bar); // removes it bidirectionally
barRepository.save(bar); // JpaRepository
然后从数据库中不删除先前创建的FooBar条目。
通过调试,我发现foo.removeBar(bar);
确实是双向删除的。没有异常。
我做错什么了吗? 我非常确定,这与级联选项有关,因为我只保存了该条。
我尝试过的:
在两个@OneToMany上都添加了orphanRemoval = true
-注释,该注释不起作用。而且我认为这是正确的,因为我既不删除也不是Foo也不是Bar,而是删除他们的关系。
从@OneToMany批注中排除CascadeType.REMOVE,但与orphanRemoval相同,我认为这不适用于这种情况。
编辑:我怀疑我的代码或模型中一定有一些东西与我的orphanRemoval混为一谈,因为现在已经有2个回答说它可行(使用orphanRemoval=true
) 。
原始问题已得到解答,但是如果有人知道是什么原因导致我的orphanRemoval无法正常工作,我将非常感谢您的投入。谢谢
代码: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
}
答案 0 :(得分:2)
我从示例代码中进行了尝试。通过几次“误入”,再现了错误。
结果确实很简单,就像添加您提到的orphanRemoval = true
一样。在Foo.getFooBars()
上:
@OneToMany(cascade = CascadeType.ALL, mappedBy = "foo", fetch = FetchType.EAGER, orphanRemoval = true)
public Collection<FooBar> getFooBars() {
return fooBars;
}
将复制内容发布到GitHub似乎是最容易的-希望还有进一步的细微差别或我错过的地方。
这基于Spring Boot和H2内存数据库,因此不应在其他环境下工作-如有疑问,请尝试mvn clean test
。
FooRepositoryTest
类具有测试用例。它具有删除链接FooBar
的验证,或者可能更容易读取记录的SQL。
修改
答案 1 :(得分:1)
我已经测试了您的方案,并进行了以下三个修改以使其起作用:
答案 2 :(得分:0)
Java持久性2.1。第3.2.3章
删除操作
•如果 X是新实体,则删除操作会将其忽略。 但是,删除操作会级联到X引用的实体, 如果从X到其他实体的关系用注释 级联=删除或级联=全部注释元素值。
•如果X为 受管实体,则删除操作会使它被删除。 如果删除操作被级联到X引用的实体, X与其他实体的关系用 级联=删除或级联=全部注释元素值。
检查是否已为实体persist
(或Foo
或FooBar
)使用操作Bar
。