【问题标题】:Cannot delete child in one-to-many relationship无法删除一对多关系中的子项
【发布时间】:2013-04-26 22:41:05
【问题描述】:

当我将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-lang EqualsBuilderHashCodeBuilder

@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();
}

膳食成分

@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

当我 从Meal.ingredientsSet 中删除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)

【问题讨论】:

    标签: java hibernate hibernate-mapping hibernate-onetomany


    【解决方案1】:

    我相信您正在寻找的解释是here。嗯,有点。不要看他的解释,这让我很困惑。不过他的例子非常好。

    所以,无论如何,我认为您想要执行以下操作之一:

    inverse=false 并从你的配料中去除饭菜成分 收集然后保存膳食

    inverse=true 并且必须将 MealIngredient 中的膳食实例变量设为 null 并保存 MealIngredient

    编辑:插入而不是更新的问题可能是由于您没有覆盖哈希码和等于。如果您使用的是 Eclipse,我相信它可以为您完成,但您必须告诉它在自动生成方法时使用复合键的两个属性。每Hibernate documentation chapter 5:

    持久化类必须重写 equals() 和 hashCode() 以 实现复合标识符相等。它还必须实施 可序列化。

    【讨论】:

    • 嗯,第一个选项就是我要找的。我尝试了许多映射变体(inversetrue/false,更改了 Meal 映射中的 cascade 选项以及 MealIngredient)。这些都没有奏效。实际上,现在它甚至不复制记录,它什么也不做(甚至不更新 Meal 中的其他属性)。关于equals()hashCode(),我确实已经实现了它们,但它仍然不起作用。
    • @mnn 您的MealIngredient.equals()MealIngredient.hacode() 方法不应使用Hibernate id 进行比较。请记住,当您第一次创建它时,如果您使用自动生成的密钥,它将是null,直到您持久化该对象。更多信息here(我认为这不会解决您的问题,但稍后会有所帮助)。
    • @mnn 从跟踪中可以看出,Hibernate 正在尝试将您的膳食成分的数量设置为 null,并且该列具有 not null。另外,在上面睡觉之后,从Meal 中删除MealIngredient 可能会导致MealIngredient 成为deleted(),因为我打赌mealId 也是not null
    • 但是当我创建 MealIngredient 时,我使用已经设置好的 mealId 创建它。我不会将其放入 Meal.ingredientsSet 中,直到将成分 ID 正确设置为现有成分。使用自动生成密钥的是 Meal 和 Ingredient,在这种情况下它们不会被创建。不,当我删除 MealIngredient 时,我只是将它从 Meal.ingredientsSet 中删除,正如你所说的那样(使用 inverse=false),它并没有从 MealIngredients 表中删除。
    【解决方案2】:

    不幸的是,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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-14
      • 2013-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多