繁体   English   中英

Hibernate具有连接类级联问题的多对多问题

[英]Hibernate Many-to-Many with join-class Cascading issue

我在FooBar之间有Many-to-Many关系。 因为我想获得有关辅助表的其他信息,所以我必须按照此处的说明创建一个辅助类FooBar使用JPA和Hibernate时,使用额外列映射多对多关联的最佳方法

我创建了一个Foo,并创建了一些条形图(保存到DB)。 当我然后使用其中一个条添加到foo

foo.addBar(bar);            // adds it bidirectionally
barRepository.save(bar);    // JpaRepository

然后创建FooBar的DB条目 - 正如预期的那样。

但是,当我想再次从foo中删除相同的栏时,使用

foo.removeBar(bar);         // removes it bidirectionally
barRepository.save(bar);    // JpaRepository

然后, 不会从数据库中删除先前创建的FooBar条目。 通过调试我看到了foo.removeBar(bar); 确实删除了双向。 没有异常被抛出。

难道我做错了什么? 我很确定它与Cascading选项有关,因为我只保存吧。


我尝试过的:

  • 在@OneToMany上添加orphanRemoval = true - 注释,这些都不起作用。 我认为这是正确的,因为我不删除 Foo和Bar,只删除他们的关系。

  • 从@OneToMany注释中排除CascadeType.REMOVE,但与orphanRemoval相同我认为这不适用于这种情况。


编辑:我怀疑在我的代码或模型中必须有一些与我的孤儿电影混淆的东西,因为现在已经有2个答案说它有效(使用orphanRemoval=true )。

最初的问题已得到解答,但如果有人知道什么可能导致我的孤儿无法工作,我会非常感谢您的意见。 谢谢


代码: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
}

我从示例代码中尝试了这个。 通过几个'草图',这再现了错误。

事实证明,解决方案就像添加你提到的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。


编辑

这是下面评论中提到的屏幕截图: deleteOrphans()断点

我已经测试了你的场景,并进行了以下三个修改以使其工作:

  1. 在Foo和Bar的两个@OneToMany getFooBars()方法中添加了orphanRemoval = true 对于您的特定情况,在Foo中添加它就足够了,但是当您从条形图中移除foo时,您可能也希望获得相同的效果。
  2. 将一个foo.removeBar(bar)调用封装在一个用Spring的@Transactional注释的方法中。 您可以将此方法放在新的@Service FooService类中。
    原因:orphanRemoval需要活动的事务会话才能工作。
  3. 删除调用barRepository.save(巴)调用foo.removeBar(巴)之后。
    现在这是多余的,因为在事务会话内部会自动保存更改。

Java Persistence 2.1。 第3.2.3节

操作删除

•如果X是新实体,则删除操作将忽略它 但是,如果从X到这些其他实体的关系使用cascade = REMOVE或cascade = ALL annotation元素值进行注释,则删除操作会级联到由X引用的实体。

•如果X是托管实体,则删除操作会导致其被删除。 如果从X到这些其他实体的关系使用cascade = REMOVE或cascade = ALL注释元素值进行注释,则删除操作将级联到由X引用的实体。

检查您是否已经为实体Foo (或FooBarBar )使用操作persist

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM