我在Many-to-ManyFoo类之间具有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
    }
    

    最佳答案

    我从示例代码中进行了尝试。在两次“误入”之后,这重现了错误。

    事实证明,该解决方案与添加您提到的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。

    编辑

    这是下面的评论中提到的屏幕截图:
    java -  hibernate 多对多连接类级联问题-LMLPHP

    关于java - hibernate 多对多连接类级联问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52442803/

    10-09 19:45