代码之家  ›  专栏  ›  技术社区  ›  kscherrer

休眠多对多与连接类级联问题

  •  6
  • kscherrer  · 技术社区  · 6 年前

    我有一个 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.addBar(bar);            // adds it bidirectionally
    barRepository.save(bar);    // JpaRepository
    

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

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

    通过调试我看到 foo.removeBar(bar);

    我做错什么了吗? 我很确定这与级联选项有关,因为我只保存了条。


    我尝试过:

    • orphanRemoval = true 在两个@OneToMany-annotations上,这都不起作用。我认为这是正确的,因为我不知道 既不是Foo也不是Bar,只是他们的关系。

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


    编辑: 我怀疑在我的代码或模型中一定有什么东西干扰了我的删除,因为现在已经有两个答案说它可以工作(与 orphanRemoval=true ).

    原来的问题已经回答,但如果有人知道什么可能导致我的孤儿搬迁不工作,我将非常感谢你的意见。谢谢


    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
    }
    
    3 回复  |  直到 6 年前
        1
  •  2
  •   df778899    6 年前

    我从示例代码中尝试了这个。用几个“草图”重现了这个错误。

    orphanRemoval = true Foo.getFooBars() :

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "foo", fetch = FetchType.EAGER, orphanRemoval = true)
    public Collection<FooBar> getFooBars() {
        return fooBars;
    }
    

    这似乎是最容易发布的复制到 GitHub

    这是基于SpringBoot和内存中的H2数据库,因此不应与其他环境一起使用—请尝试 mvn clean test 如果有疑问。

    这个 FooRepositoryTest FooBar ,或者更容易读取记录的SQL。


    编辑

    deleteOrphans() breakpoint

        2
  •  1
  •   Andrei Pietrusel    6 年前

    1. 补充 对双方来说 来自Foo和Bar的getFooBars()方法。对于您的特定场景,将其添加到Foo中就足够了,但是您可能也希望在从栏中移除Foo时获得相同的效果。
    2. 将foo.removeBar(bar)调用包含在用Spring的 @事务性 @服务 餐饮服务 班级。
      原因:孤立删除需要一个活动的事务会话才能工作。
    3. 已删除对的呼叫 barRepository.save(巴) foo.removeBar(巴) .
        3
  •  0
  •   Community CDub    5 年前

    Java持久性2.1。第3.2.3章

    删除操作

    X是一个新实体,它被remove操作忽略 . 但是,remove操作被级联到X引用的实体, 如果从X到这些其他实体的关系用 cascade=REMOVE或cascade=ALL注释元素值。

    如果X是 对于托管实体,remove操作会导致其被删除。

    检查您是否已使用该操作 persist Foo (或 FooBar Bar ).