2013-04-26 5 views
0

Когда я положил inverse=true в set, ничего не удаляется. Когда у меня нет, и я удалить MealIngredient из set, то 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> 

Да, отношения между Meal и Ingredient - много-ко многим с таблицей объединения MealIngredient (и да, я должен также нанести MealIngredient из-за дополнительных столбцов в этой таблице).

Этот question не помог мне, не сделал this.

Редактировать: Только вставка работ с текущим сопоставлением, обновление только генерирует другую строку в таблице MealIngredient.


Edit 2:hashCode и equals реализации:

MealIngredient $ Id: (использует Apache commons-langEqualsBuilder и HashCodeBuilder)

@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 делает под капотом, но это сделать insert в MealIngredient:

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 из Meal.ingredientsSet, 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) и работать с этим.

После того, как я использую дополнительный идентификатор в качестве первичного ключа, вставка/обновление/удаление работает, как и ожидалось (даже с cascade, установленным на delete-orphan, каскадное удаление работает!).

Я предоставляю окончательные сопоставления для объектов Meal и MealIngredient, для дальнейшего использования. Я надеюсь, что это поможет другим, когда они наткнутся на отношения «многие ко многим» с дополнительными свойствами/столбцами в таблице соединений.

<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 из ваших ингредиентов коллекции, а затем сохранить Meal

inverse=true и иметь нулевое значение экземпляра еды переменная в MealIngredient и сохранить MealIngredient

EDIT: проблема со вставками вместо обновлений, вероятно, из-за факт, что вы не перегружены hashcode и равны.Если вы используете Eclipse, я считаю, что он может сделать это за вас, но вы должны сказать ему использовать оба свойства вашего составного ключа, когда он автоматически генерирует методы. Пер Hibernate documentation chapter 5:

Упорное класс должен переопределить равных() и хэш-код() для осуществления составного равенства идентификатора. Он также должен реализовать Serializable.

+0

Ну, первый вариант - это то, что я ищу. Я пробовал много вариантов сопоставлений ('inverse' с' true'/'false', измененными параметрами' cascade' в картотеке «Meal», а также 'MealIngredient'). Ничего из этого не сработало. Фактически, теперь он даже не дублирует запись, он просто ничего не делает (даже не обновляет другие свойства в Meal). И что касается 'equals()' и 'hashCode()', то я _do_ реализовал их, и он все равно не работает. – mnn

+0

@mnn Методы 'MealIngredient.equals()' и 'MealIngredient.hacode()' не должны использовать идентификатор Hibernate для сравнения. Помните, что когда вы впервые создадите его, если вы используете автогенерированный ключ, это будет «null», пока вы не сохраните объект. Подробнее [здесь] (https://community.jboss.org/wiki/EqualsAndHashCode) (я не думаю, что это исправит вашу проблему, но это поможет позже). –

+0

@mnn Из трассы, похоже, что Hibernate пытается установить количество вашего файла еды в поле «null», и этот столбец «не равен нулю». Кроме того, после спящего режима на нем удаление «MealIngredient» из «Meal» должно, вероятно, привести к тому, что 'MealIngredient' будет' deleted() ', так как я поставил' mealId' 'также не равно null. –

Смежные вопросы