2013-06-28 48 views
0

我試圖刪除使用entityManager.remove(parent)具有一對多的父子關係的父實體。但是,從SQL日誌中,我看到OpenJPA首先調用UPDATE,並試圖爲NOT NULL字段設置爲空。爲什麼OpenJPA在DELETE之前調用UPDATE?

executing prepstmnt 773394836 UPDATE child SET parent_id = ? WHERE parent_id = ? [params=(null) null, (long) 16] 
executing prepstmnt 1127292223 DELETE FROM parent WHERE id = ? [params=(long) 16] 
executing prepstmnt 1297966852 DELETE FROM child WHERE lookup_id = ? AND parent_id = ? [params=(int) 1, (long) 16] 

如何配置我的班,以防止這種第一UPDATE電話?

我的課:

@Entity 
@Table(name = "parent") 
public class Parent { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id") 
    private long id; 

    private String name; 

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, 
     orphanRemoval = true) 
    @JoinColumn(name = "parent_id") 
    private List<Child> children; 

    // getters, etc. 
} 

@Entity 
@Table(name = "child") 
@IdClass(ChildPrimaryKey.class) 
public class Child { 

    @Id 
    @Column(name = "parent_id", nullable = false, updatable = false) 
    private long parentId; 

    @Id 
    @Column(name = "lookup_id") 
    private int lookupId; 

    @ManyToOne 
    @JoinColumn(name = "parent_id", referencedColumnName = "id", 
     nullable = false, insertable = false, updatable = false) 
    private Parent parent; 

    // getters, etc. 
} 

我的數據庫表(在MySQL):

CREATE TABLE IF NOT EXISTS `parent` (
    `id` BIGINT NOT NULL AUTO_INCREMENT , 
    `name` VARCHAR(50) NOT NULL , 
    PRIMARY KEY (`id`)) 
ENGINE = InnoDB; 

CREATE TABLE IF NOT EXISTS `child` (
    `lookup_id` INT NOT NULL , 
    `parent_id` BIGINT NOT NULL , 
    PRIMARY KEY (`parent_id`, `lookup_id`) , 
    INDEX `fk_library_code_idx` (`parent_id` ASC) , 
    CONSTRAINT `fk_library_code` 
    FOREIGN KEY (`parent_id`) 
    REFERENCES `parent` (`id`) 
    ON DELETE CASCADE 
    ON UPDATE CASCADE) 
ENGINE = InnoDB; 

回答

2

我不是究竟是確定是什麼導致JPA決定執行這些查詢,但是,我注意到你的映射有幾個問題。

首先,您沒有在@OneToMany註釋中指定mappedBy元素(即,您不告訴OpenJPA Child是關係的所有者)。這是我第一次猜測你爲什麼看到這種行爲。從API specification

如果關係是雙向的,所述的mappedBy元件必須是用於指定 是關係的所有者的實體的關係字段或屬性 。

爲了解決這個問題,改變你的Parent@OneToMany到包括mappedBy元素,丟棄@JoinColumn

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, 
      fetch = FetchType.EAGER, orphanRemoval = true) 
    private List<Child> children; 

此外,我注意到你正在嘗試使用Parent的ID作爲部分的Child的ID,並有Child映射到相同的PK列(parentparentId)上的兩個字段。處理這種情況的最佳方法是使用@MapsId

@Id 
    private long parentId; 

    // ... 

    @MapsId("parentId") 
    @ManyToOne 
    @JoinColumn(name = "parent_id", referencedColumnName = "id", nullable = false) 
    private Parent parent; 
+0

使用mappedBy代替@JoinColumn會導致插入異常,因爲OpenJPA會嘗試將0設置爲parent_id:引發原因:org.apache.openjpa.lib.jdbc.ReportingSQLException:無法添加或更新子行:外鍵約束失敗(''test''.''child'',CONSTRAINT''fk_library_code'' FOREIGN KEY(''parent_id'')REFERENCES''parent''(''id'')ON DELETE CASCADE ON UPDATE CASCADE){prepstmnt 972578961插入到孩子(lookup_id,parent_id)VALUES(?,?)[params =(int)1,(long)0]} [code = 1452,state = 23000]'。我認爲mappedBy是用於連接表的。 –

+0

在進一步研究之後,使用'mappedBy'是在這種情況下正確的方法。但是,兒童和父母實體必須分開管理。例如,'parent_id'必須設置爲正確的值,子實體必須手動刪除等。在這種情況下'@ MapsId'不需要。 –

+0

@VictorLyuboslavsky如果您使用'@ MapsId','parentId'會自動從'Parent'複製給您。您的用例與[documentation](http://docs.oracle.com/javaee/6/api/javax/persistence/MapsId.html)中描述的內容完全相同:「_設計ManyToOne或OneToOne關係屬性,該屬性提供映射...父實體的簡單主鍵「。此外,你不應該分開移除孩子......你能顯示你的更新實體嗎? – DannyMo

0

這是一個假設。

我認爲正在發生的事情是UPDATE語句在執行DELETE時避免違反約束。或者至少,這就是爲什麼OpenJPA 認爲它需要做更新。

有兩種可能性:

  • OpenJPA的是權利和更新是必要的
  • OpenJPA的是錯的,UPDATE是多餘的

我建議您設置數據庫與一個相關的「父」和「子」行,然後嘗試手動刪除一個或其他行沒有做更新。這將確定是否需要更新確實

根據結果,要麼忽略問題,嘗試更改模式和/或註釋,或者查看OpenJPA問題跟蹤器中是否存在錯誤報告/解決方法。 (你也可以嘗試一些實驗來看看Hibernate是否行爲相同或/和這是否特定於MySQL。)

+0

我懷疑的更新失敗(該公司試圖以一個PK/NOT NULL列設置爲NULL) – DannyMo

+0

我試圖手動刪除數據庫中。只需要從DELETE FROM parent WHERE id =?'進行刪除。更新不是必需的。孩子的第二個DELETE也是沒有必要的。 –

相關問題