2016-06-08 191 views
1

我正在使用Spring Data和休眠,CascadeType.ALLorphanRemoval = true休眠刪除非孤兒

問題是,當將子實體從parentX移動到parentY時,如果parentY在parentX之前持久化,則Hibernate將從子數據庫中刪除子實體。之後,孩子仍然存在於parentY內存中。如果它被刪除,並且parentY保存,則拋出EntityNotFoundException

我有一個SSCE證明這一點,可以發佈它,如果有必要,但它似乎是一個簡單的問題。

父實體:

@Entity 
public class TestParent implements Serializable { 

    private static final long serialVersionUID = 3572015072906463953L; 

    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TestParent_GENERATOR") 
    @SequenceGenerator(name = "TestParent_GENERATOR", initialValue = 1, sequenceName = "TestParent_SEQUENCE", allocationSize = 1) 
    private long id; 
    private String name; 

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) 
    @JoinColumn(name = "TestParent_Id") 
    private Set<TestChild> testChildren = new HashSet<>(); 

    @SuppressWarnings("unused") 
    private TestParent() { 
    } 

    public TestParent(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return this.name; 
    } 

    public void addChild(TestChild child) { 
     this.testChildren.add(child); 
    } 

    public void removeChild(TestChild child) { 
     this.testChildren.remove(child); 
    } 

    public TestChild findChild(String childsName) { 
     for (TestChild testChild : this.testChildren) { 
      if (testChild.getName().equals(childsName)) { 
       return testChild; 
      } 
     } 
     return null; 
    } 
} 

子實體:

@Entity 
public class TestChild implements Serializable { 

    private static final long serialVersionUID = -1594688339088954284L; 

    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TestChild_GENERATOR") 
    @SequenceGenerator(name = "TestChild_GENERATOR", initialValue = 1, sequenceName = "TestChild_SEQUENCE", allocationSize = 1) 
    private long id; 

    private String name; 

    @SuppressWarnings("unused") 
    private TestChild() { 
    } 

    public TestChild(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return this.name; 
    } 
} 
+0

請提供您的實體代碼。 –

+0

'TestChild'是否有關聯到'TestParent'(也許通過一個字段)?發佈的代碼不顯示任何這樣的關係。 – manish

+0

@Eruza,你能查看下面我的答案鏈接的示例應用程序嗎? – manish

回答

2

實體映射是從Hibernate的角度來看,這就是爲什麼你可能會得到意想不到的結果不完整。最大的罪魁禍首是orphanRemoval = true,它已被使用而未使用mappedBy = ...。雖然JPA規範並不要求mappedBy = ...orphanRemoval = true一起指定,但如果未指定mappedBy = ...,Hibernate無法確定一對多關聯的many一側上的實體是否真的孤立。

下面的映射將正確的行爲:

class TestParent { 
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "testParent") 
    private Set<TestChild> testChildren = new HashSet<TestChild>(); 
} 

class TestChild { 
    @JoinColumn(name = "TestParent_Id") 
    @ManyToOne 
    private TestParent testParent; 
} 

注意@JoinColumn(name = "TestParent_Id")需要被移動到@ManyToOne側。

您還需要非常小心父母的變化。如果孩子留在前父母的children集合中,則更改將不會生效。


我創建了一個演示正在運行的JPA配置的sample project。該項目包含模擬以下情況的單元測試:

  1. A Child實例c已創建。
  2. A Parent實例a已創建。
  3. Child實例c被添加/分配給Parent實例a
  4. a已保存。這種級聯降至c,這也得到保存。
  5. 另一個Parent實例b已創建。
  6. Child實例c被添加/分配給Parent實例b
  7. b已保存。這種級聯降至c,這也得到保存。

在這種情況下,我們希望得到執行下面的SQL查詢:

INSERT INTO parent (...) VALUES (...); 
INSERT INTO child (...) VALUES (...); 
INSERT INTO parent (...) VALUES (...); 
UPDATE child SET ...; 

如果您運行單元測試爲mvn clean test,你會看到如預期正在執行的SQL查詢。