2013-04-26 65 views
0

當我把inverse=true寫入set時,什麼都不會被刪除。當我不這樣做,我從set刪除MealIngredient,則Hibernate嘗試設置null,它失敗並拋出異常:不能刪除一對多關係中的孩子

[SQLITE_CONSTRAINT] Abort due to constraint violation (MealIngredients.mealId may not be NULL) 

下面是XML映射:

<class name="restaurant.meal.Meal" table="Meals"> 
    <id name="id" type="integer"> 
     <column name="id" not-null="true" unique="true"/> 
     <generator class="increment"/> 
    </id> 
    <!-- some other, simple properties --> 

    <set name="ingredientsSet" cascade="all" lazy="false"> 
     <key> 
      <column name="mealId" not-null="true" /> 
     </key> 
     <one-to-many class="restaurant.meal.MealIngredient" /> 
    </set> 
</class> 

<class name="restaurant.meal.MealIngredient" table="MealIngredients"> 
    <composite-id name="id" class="restaurant.meal.MealIngredient$Id"> 
     <key-property name="ingredientId" /> 
     <key-property name="mealId" /> 
    </composite-id> 
    <many-to-one name="ingredient" class="restaurant.storage.Ingredient" insert="false" update="false" lazy="false"> 
     <column name="ingredientId" not-null="true" /> 
    </many-to-one> 
    <many-to-one name="meal" class="restaurant.meal.Meal" insert="false" update="false" lazy="false"> 
     <column name="mealId" not-null="true" /> 
    </many-to-one> 
    <!-- other properties --> 
</class> 

是的,關係在MealIngredient之間是多對多連接表MealIngredient(是的,我必須映射MealIngredient,因爲該表中有附加列)。

question對我沒有幫助,this也沒有幫助。

編輯: 只有插入與當前映射一起工作,更新只是在MealIngredient表中生成另一行。


編輯2:hashCodeequals實現:

MealIngredient的$ id:(使用Apache commons-langEqualsBuilderHashCodeBuilder

@Override 
public boolean equals(Object o) { 
    if(!(o instanceof Id)) 
     return false; 

    Id other = (Id) o; 
    return new EqualsBuilder() 
       .append(this.getMealId(), other.getMealId()) 
       .append(this.getIngredientId(), other.getIngredientId()) 
       .isEquals(); 
} 

@Override 
public int hashCode() { 
    return new HashCodeBuilder() 
       .append(this.getMealId()) 
       .append(this.getIngredientId()) 
       .hashCode(); 
} 

MealIngredient

@Override 
public boolean equals(Object o) 
{ 
    if(!(o instanceof MealIngredient)) 
     return false; 

    MealIngredient other = (MealIngredient) o; 
    return this.getId().equals(other.getId()); 
} 

@Override 
public int hashCode() 
{ 
    return this.getId().hashCode(); 
} 

我檢查日誌,雖然我不知道什麼Hibernate的引擎蓋下做的,但它確實讓insertMealIngredient

15:42:53,122 TRACE IntegerType:172 - returning '5' as column: quantity3_ 
Hibernate: 
    insert 
    into 
     MealIngredients 
     (quantity, ingredientId, mealId) 
    values 
     (?, ?, ?) 
15:42:53,131 TRACE IntegerType:133 - binding '16' to parameter: 1 
15:42:53,131 TRACE IntegerType:133 - binding '5' to parameter: 2 
15:42:53,131 TRACE IntegerType:133 - binding '1' to parameter: 3 

;當我從刪除MealIngredient Hibernate使update並嘗試將mealId設置爲null

Hibernate: 
    update 
     MealIngredients 
    set 
     quantity=? 
    where 
     ingredientId=? 
     and mealId=? 
15:48:57,529 TRACE IntegerType:126 - binding null to parameter: 1 
15:48:57,529 TRACE IntegerType:133 - binding '1' to parameter: 2 
15:48:57,531 TRACE IntegerType:133 - binding '1' to parameter: 3 
15:48:57,535 WARN JDBCExceptionReporter:77 - SQL Error: 0, SQLState: null 
15:48:57,535 ERROR JDBCExceptionReporter:78 - [SQLITE_CONSTRAINT] Abort due to constraint violation (MealIngredients.quantity may not be NULL) 

回答

0

不幸的是,似乎Hibernate不能很好地處理複合主鍵。我不得不在多對多連接表中添加額外的ID列(如我的MealIngredient)並使用它。

當我使用額外的ID作爲主鍵後,插入/更新/刪除按預期工作(即使cascade設置爲delete-orphan,級聯刪除工作!)。

我爲實體MealMealIngredient提供最終映射,以備將來參考。我希望這會幫助其他人,當他們偶然發現與連接表中的其他屬性/列之間的多對多關係時。

<class name="restaurant.meal.Meal" table="Meals"> 
    <id name="id" type="integer"> 
     <column name="id" not-null="true" unique="true"/> 
     <generator class="increment"/> 
    </id> 
    <!-- additional properties --> 

    <set name="ingredientsSet" table="MealIngredients" cascade="all-delete-orphan" lazy="false" inverse="true"> 
     <key update="true"> 
      <column name="mealId" not-null="true" /> 
     </key> 
     <one-to-many class="restaurant.meal.MealIngredient" /> 
    </set> 
</class> 
<class name="restaurant.meal.MealIngredient" table="MealIngredients"> 
    <id name="id" type="integer"> 
     <column name="id" not-null="true" unique="true"/> 
     <generator class="increment"/> 
    </id> 
    <many-to-one name="ingredient" column="ingredientId" not-null="true" class="restaurant.storage.Ingredient" lazy="false" /> 
    <many-to-one name="meal" column="mealId" not-null="true" class="restaurant.meal.Meal" lazy="false" /> 

    <!-- additional properties --> 
</class> 
0

我相信你要找的解釋是here。好吧,有點。不要讀他的解釋,它會讓我困惑。儘管如此,他的例子很棒。

所以,不管怎麼說,我覺得你想要做下列操作之一:

inverse=false並從成分中刪除mealIngredient 收集並保存了一頓

inverse=true,並具有爲null餐實例在MealIngredient變量並保存MealIngredient

編輯:以插入,而不是更新的問題,可能是由於事實上你沒有覆蓋hashcode和equals。如果您使用的是Eclipse,我相信它可以爲您做到這一點,但您必須告訴它在自動生成方法時使用組合鍵的兩個屬性。每Hibernate documentation chapter 5

的持久化類必須重載equals()和hashCode()來 實現組合的標識符平等。它還必須實現 可序列化。

+0

那麼,第一個選項是我在找什麼。我已經嘗試了許多變種的映射(''inverse'用'true' /'false',改變'Meal'映射中的'cascade'選項以及'MealIngredient')。沒有任何工作。實際上,現在它甚至不重複記錄,它只是不做任何事情(甚至不更新Meal中的其他屬性)。關於'equals()'和'hashCode()',我已經實現了它們,但它仍然不起作用。 – mnn 2013-04-27 13:09:49

+0

@mnn您的'MealIngredient.equals()'和'MealIngredient.hacode()'方法不應該使用Hibernate id進行比較。請記住,當你第一次創建它時,如果你使用了一個自動生成的鍵,那麼它將會是'null',直到你堅持這個對象。更多信息[這裏](https://community.jboss.org/wiki/EqualsAndHashCode)(我認爲這不會解決您的問題,但稍後會有所幫助)。 – 2013-04-27 14:48:15

+0

@mnn從跟蹤中看來,Hibernate看起來像是試圖將你的mealIdredient的數量設置爲null,並且該列有'not null'。另外,在睡覺之後,從「Meal」中刪除「MealIngredient」應該可能導致「MealIngredient」被「刪除()」,因爲我敢打賭「mealId」也是「非空」。 – 2013-04-27 15:03:10

相關問題