【问题标题】:Can I add 'ON DELETE CASCADE' to tables managed by Hibernate?我可以在 Hibernate 管理的表中添加“ON DELETE CASCADE”吗?
【发布时间】:2013-11-10 07:16:47
【问题描述】:

我有一些由 Hibernate 管理的具有各种外键约束的表。 Cascade on delete 目前由 Hibernate 单独管理。为了玩弄测试数据,我经常手动创建和删除一些行。如果我可以将 ON DELETE CASCADE 添加到外键约束中,这将对我有很大帮助,但我不知道 Hibernate 是否会遇到此问题,因为数据库会在 Hibernate 之前删除内容。


很多人似乎都专注于 DDL。我的意图是不指示 Hibernate 使用 SQL DELETE CASCADE 创建 DDL。我只想知道如果我在数据库中指定一个 ON DELETE CASCADE 除了在参考注释上使用 JPA 的 cascade = CascadeType.REMOVE 是否会造成任何伤害,例如 @ManyToOne。

【问题讨论】:

标签: java sql hibernate jpa


【解决方案1】:

您可以使用CascadeType.DELETE,但是此注释仅适用于EntityManager 中的对象,而不适用于数据库。您要确保将ON DELETE CASCADE 添加到数据库约束中。要进行验证,您可以配置 JPA 以生成 ddl 文件。查看ddl 文件,您会注意到ON DELETE CASCADE 不是约束的一部分。将ON DELETE CASCADE 添加到ddl 文件中的实际SQL,然后从ddl 更新您的数据库架构。这将解决您的问题。

这个link 展示了如何在 MySQL 中为CONSTRAINT 使用ON DELETE CASCADE。您在约束上执行此操作。您也可以在 CREATE TABLE 或 ALTER TABLE 语句中执行此操作。 JPA 很可能在 ALTER TABLE 语句中创建了约束。只需在该语句中添加ON DELETE CASCADE。

请注意,一些 JPA 实现者确实为此功能提供了一种方法。

Hibernate 确实使用 @OnDelete 注释提供此功能,因此如果您想坚持使用标准 JPA 功能,最好使用此功能或简单地更新 ddl 文件。

【讨论】:

    【解决方案2】:

    我发现两个潜在问题:

    1. 如果表示您在数据库中直接将操作级联到的表的实体是版本化的,那么它将无法工作,因为当 Hibernate 尝试自行删除记录时,版本检查将失败(Hibernate 将假定已经有并发线程更新或删除相应记录)。
    2. 如果在某些用例中,您的业务逻辑在从父级级联删除这些实体实例后重新保留这些实体实例(例如,您正在删除旧的父级并将关联的子级迁移到新的父级,尽管为了更好清晰度如果关联存在这样的用例,我根本不会级联删除,但这取决于您,因为它是 JPA 规范所允许的),然后 Hibernate 将只是 un-schedule 删除子级并仅删除父级,所以如果你在数据库中级联删除,你仍然会得到删除的孩子。

    可能还有其他一些情况在某些情况下可能会出现问题,所以我建议不要这样做。

    【讨论】:

      【解决方案3】:

      您可以使用本机数据库功能在删除父记录时删除子记录。

      注意双向关系,并确保您只在级联中指定插入和更新(为了更安全)。

      【讨论】:

        【解决方案4】:

        您出于测试目的提及。我猜,执行一些测试,删除数据,重放测试......

        当使用二级缓存或查询缓存时,如果您直接从数据库中删除数据,缓存将会失效。 这可能会导致意外的测试结果。

        所以是的,如果您使用二级/查询缓存,这将与 Hibernate 冲突,因为实体不会从缓存中被逐出。确保在直接删除任何数据后清除所有缓存。 See this question on how to clear cache.

        Hibernate 官方docs 也提到了这一点:

        请注意,缓存不知道其他应用程序对持久存储所做的更改。但是,它们可以配置为定期使缓存数据过期。

        【讨论】:

          【解决方案5】:

          只要您没有启用CascadeType.REMOVE 或orphanRemoval 以防止实体移除事件的传播,您就可以安全地使用ON DELETE CASCADE。

          在你的关系上拥有@OnDelete(action = OnDeleteAction.CASCADE) 将强制Hibernate 为相关的外键生成ON DELETE CASCADE。但是,您的 Dialect 必须通过在 supportsCascadeDelete 方法中返回 true 来表明支持这种关系。

          Dialect.supportsCascadeDelete()

          OnDelete

          【讨论】:

          • 感谢您的回答。我本来希望 Hibernate 能够处理方言的支持并以任何一种方式做正确的事情。显然,人们需要意识到这一点。我很高兴将 ORM 抛在身后的另一个原因......我希望我再也不用处理它们了。
          【解决方案6】:

          根据我的实验,由于我已经为此苦苦挣扎了几个小时,所以在 @JoinColumn 中使用 updatable = false 会阻止 Hibernate 这样做

          update theTable set theReference = null where theIdentifier = ?
          

          例如(在 Kotlin 中):

          @Entity
          @Table(name = "course")
          class Course(
              @Column(name = "title")
              var title: String? = null,
              @OneToMany(cascade = [CascadeType.PERSIST])
              @JoinColumn(name = "course_id", updatable = false)
              val reviews: MutableList<Review> = mutableListOf()
          ) {
          
              @Id
              @GeneratedValue(strategy = GenerationType.IDENTITY)
              @Column(name = "id")
              var id: Int? = null
          
          }
          

          【讨论】:

            【解决方案7】:

            不要使用 cascade = CascadeType.REMOVE Documentation here

            因为你的数据库可能会被破坏。您可以使用正式订单。删除子稳定 然后删除主表

            【讨论】:

            • 问题不在于删除表。这是关于存储在表中的数据。
            猜你喜欢
            • 2016-09-28
            • 1970-01-01
            • 1970-01-01
            • 2015-01-05
            • 2012-07-16
            • 1970-01-01
            • 2019-01-22
            • 2010-12-06
            • 1970-01-01
            相关资源
            最近更新 更多