【问题标题】:Getting Hibernate to cascade on save, but not delete?让休眠在保存时级联,但不删除?
【发布时间】:2012-11-05 11:29:36
【问题描述】:

所以我的休眠实现有问题。当我尝试删除父类时,在级联层次结构深处的类上收到外键约束异常。在我详细介绍之前,我将首先描述类的关系,因为它关系到它们需要如何保存和删除。

在顶层,我有一个 Customer 类,其中包含一个 DefaultMask 对象列表。这是主列表,因为这些默认掩码由我的对象层次结构中的其他类使用,但始终来自此列表。掩码仅在此列表中创建并从此列表中删除。

在层次结构的更下方,我有一个 Column 类,它可以(可选地)在其上设置一个 DefaultMask。更简洁地描述这种关系;

客户拥有零到多个 DefaultMask。 客户拥有零到多列。 一列可能有一个 DefaultMask。

在我的应用程序中,当我尝试删除客户时,异常来自 Column 类到 DefaultMask 类的外键约束,我认为问题在于 CascadeType 的设置不正确。我已经研究了这个问题并找到了关于名为 mappedBy 的属性和使用 Hibernate 自己的 CascadeType.SAVE_UPDATE 的信息(为了防止 Hibernate 尝试删除由 Column 持有的 DefaultMask),但我承认我在这里有点迷路并且可以使用一些直接的指导。类的相关代码和实际的异常消息如下。

客户:

@Entity
public class Customer {

@Id
private String id;
@OneToMany(cascade = CascadeType.ALL)
private List<DefaultMask> masks;
    //(Columns are held further down in hierarchy)

列:

@Entity
@Table(name = "WarehouseColumn")
public class Column implements Comparable<Column> {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int hibernateID;
@OneToOne
private DefaultMask mask;

默认掩码:

@Entity
public class DefaultMask implements Mask {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int hibernateID;
private String type;
private String mask;

异常信息:

org.hibernate.exception.ConstraintViolationException: 无法删除或更新父行:外键约束失败 (hibernate.WarehouseColumn, CONSTRAINT FK8BB153D994AD57D3 FOREIGN KEY (mask_hibernateID) REFERENCES DefaultMask (@ 987654329@)) 引起:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:无法删除或更新父行:外键约束失败(hibernate.WarehouseColumn,约束FK8BB153D994AD57D3外键(mask_hibernateID)参考@ 987654334@(hibernateID))

【问题讨论】:

    标签: java hibernate cascade


    【解决方案1】:

    您正试图删除一个客户,这会自动删除其默认掩码列表。但是其中一个掩码被一列引用。所以数据库(以及 Hibernate)拒绝执行删除,因为它会使列处于不一致的状态:它会引用一个不再存在的默认掩码。

    所以你有几个功能选择:

    • 保持原样:无法删除客户,因为其掩码之一仍被列引用
    • 删除级联:删除客户将删除客户,但不会删除其掩码。
    • 找到所有引用要删除的用户的任何默认掩码的列,并删除这些列。然后,删除用户及其默认掩码
    • 找到所有引用要删除的用户的任何默认掩码的列,并将其掩码字段设置为空。然后,删除用户及其默认掩码

    【讨论】:

    • 抱歉,我没有明确表示列也归客户对象所有。我将编辑我的问题以使其更加明显。因此,如果使用其掩码删除客户,则其所有引用这些掩码的列也将被级联删除。
    • 那么还应该有从客户到列,从列到默认掩码的级联。
    【解决方案2】:

    级联与逻辑所有权的概念密切相关。

    基本上,您需要选择以下选项之一:

    • Customer 逻辑上拥有它的DefaultMasks。在这种情况下,您希望在删除Customer 时删除DefaultMasks,因此您需要使用CascadeType.ALL。由于Column 引用DefaultMask,它可能也归Customer 所有,应该删除到

      可以通过使用DefaultMaskColumn之间的双向关系并适当级联来实现,如下:

      @Entity
      public class DefaultMask implements Mask {
          @OneToOne(mappedBy = "mask", cascade = CascadeType.ALL)
          Column column;
          ...
      }
      
    • DefaultMasks 本身就是实体,Customer 只是引用现有的DefaultMasks。在这种情况下,您可能根本不需要对这种关系使用级联。

    【讨论】:

    • 列确实也归客户所有,我没有在我的问题中明确表示,抱歉。我将如何设置您提到的 DefaultMask 和 Column 之间的双向关系?我应该提一下,我们的 Hibernate 实现取代了 DB4O 实现(面向对象的数据库),虽然 Column 与 DefaultMask 具有 OneToOne 关系,但 DefaultMask 并没有引用所有使用它的 Column。 DefaultMask 确实包含拥有它的客户的 ID,但仅此而已。
    • @DavidH:已更新。您必须添加从 DefaultMaskColumn 的引用,以便在该方向配置级联(我假设您具有一对一的关系,正如您在 Column 中指定的那样)。
    • 一个 DefaultMask 可以被多个 Columns 使用,所以关系是 OneToMany。所以你说的是,当我删除客户时,Hibernate 正确地级联删除客户持有的 DefaultMask,但是因为这些 DefaultMask 被 Columns 引用(也将被删除),所以抛出异常,因为 Hibernate 没有先删除那些列?如果是这种情况,有没有办法告诉 Hibernate 在尝试删除掩码之前先级联到删除列?
    猜你喜欢
    • 2012-01-21
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 2022-12-03
    • 2011-12-01
    • 2011-04-12
    • 1970-01-01
    相关资源
    最近更新 更多