簡體   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