Grails 中没有双向关系的级联删除

发布于 2024-11-26 01:14:07 字数 2328 浏览 3 评论 0原文

我正在使用 Grails 1.3.7,并且具有以下域类:

package com.fxpal.test

class UserGroup {

    String name

    static constraints = {
        name(blank: false)
    }
}

class Invitation {

    UserGroup group
    String user

    static belongsTo = [group: UserGroup]

    static constraints = {
        group(nullable: false)
    }
}

我希望能够在删除 UserGroup 实例时删除引用该 UserGroup 实例的所有 Invitation 实例,而不具有引用 UserGroup 中的 Invitation 的显式关系。换句话说,我希望从 UserGroup 到 Invitation 进行级联删除,而不修改 Group。

由于表示邀请 -> 的约束,我对此的测试失败了。用户组关系:

void testCascadingDelete() {
    UserGroup group1 = new UserGroup(name: 'group1').save(flush: true, failOnError: true)
    UserGroup group2 = new UserGroup(name: 'group2').save(flush: true, failOnError: true)

    Invitation invitation = new Invitation(user:'user1', group: group1).save(flush: true, failOnError: true)

    assertEquals("Wrong number of groups.", 2, UserGroup.count())
    assertEquals("Wrong number of invitations.", 1, Invitation.count())

    group1.delete(flush: true, failOnError: true)

    assertEquals("Wrong number of groups.", 1, UserGroup.count())
    assertEquals("Wrong number of invitations.", 0, Invitation.count())
}

当我运行测试时,它失败如下:

could not delete: [com.fxpal.test.UserGroup#1]; SQL [delete from user_group where id=? and version=?]; constraint [FK473F7799A8642225]; nested exception is org.hibernate.exception.ConstraintViolationException: could not delete: [com.fxpal.test.UserGroup#1]
org.springframework.dao.DataIntegrityViolationException: could not delete: [com.fxpal.test.UserGroup#1]; SQL [delete from user_group where id=? and version=?]; constraint [FK473F7799A8642225]; nested exception is org.hibernate.exception.ConstraintViolationException: could not delete: [com.fxpal.test.UserGroup#1]
    at com.fxpal.test.InvitationIntegrationTests.testCascadingDelete(InvitationIntegrationTests.groovy:23)
Caused by: org.hibernate.exception.ConstraintViolationException: could not delete: [com.fxpal.test.UserGroup#1]
    at com.fxpal.test.InvitationIntegrationTests.testCascadingDelete(InvitationIntegrationTests.groovy:23)
  ...

它看起来像规范的鼻脸示例,但级联删除似乎不起作用。我真的不想在 UserGroup 中表示 Invitation,特别是因为最终 Invitation 将引用其他几个域类,删除其中任何一个都会导致相应的 Invitation 也被删除。

我缺少什么?

基因

I am using Grails 1.3.7, and have the following domain classes:

package com.fxpal.test

class UserGroup {

    String name

    static constraints = {
        name(blank: false)
    }
}

class Invitation {

    UserGroup group
    String user

    static belongsTo = [group: UserGroup]

    static constraints = {
        group(nullable: false)
    }
}

I would like to be able to delete all Invitation instances that refer to a UserGroup instance when that UserGroup instance is deleted, without having an explicit relationship that refers to Invitation in UserGroup. In other words, I would like to have a cascading delete from UserGroup to Invitation, without modifying Group.

My test for this fails due to a constraint that represents the Invitation -> UserGroup relationship:

void testCascadingDelete() {
    UserGroup group1 = new UserGroup(name: 'group1').save(flush: true, failOnError: true)
    UserGroup group2 = new UserGroup(name: 'group2').save(flush: true, failOnError: true)

    Invitation invitation = new Invitation(user:'user1', group: group1).save(flush: true, failOnError: true)

    assertEquals("Wrong number of groups.", 2, UserGroup.count())
    assertEquals("Wrong number of invitations.", 1, Invitation.count())

    group1.delete(flush: true, failOnError: true)

    assertEquals("Wrong number of groups.", 1, UserGroup.count())
    assertEquals("Wrong number of invitations.", 0, Invitation.count())
}

When I run the test, it fails like this:

could not delete: [com.fxpal.test.UserGroup#1]; SQL [delete from user_group where id=? and version=?]; constraint [FK473F7799A8642225]; nested exception is org.hibernate.exception.ConstraintViolationException: could not delete: [com.fxpal.test.UserGroup#1]
org.springframework.dao.DataIntegrityViolationException: could not delete: [com.fxpal.test.UserGroup#1]; SQL [delete from user_group where id=? and version=?]; constraint [FK473F7799A8642225]; nested exception is org.hibernate.exception.ConstraintViolationException: could not delete: [com.fxpal.test.UserGroup#1]
    at com.fxpal.test.InvitationIntegrationTests.testCascadingDelete(InvitationIntegrationTests.groovy:23)
Caused by: org.hibernate.exception.ConstraintViolationException: could not delete: [com.fxpal.test.UserGroup#1]
    at com.fxpal.test.InvitationIntegrationTests.testCascadingDelete(InvitationIntegrationTests.groovy:23)
  ...

It seems like the canonical nose-face example, but the cascading delete doesn't seem to work. I really don't want to have to represent the Invitation in the UserGroup, in particular because in the end, the Invitation will reference several other domain classes, the deletion of any of which should cause the corresponding Invitation to get deleted as well.

What am I missing?

Gene

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

权谋诡计 2024-12-03 01:14:07

我不确定在没有双向关系的情况下是否可能,但您可以轻松地在事务服务方法中自己完成(以确保删除全部发生或没有发生):

void deleteGroup(UserGroup group) {
   Invitation.executeUpdate(
       'delete from Invitation where group=:group',
       [group: group])
   group.delete(flush: true, failOnError: true)
}

然后您的测试变成了

def fooService

...

void testCascadingDelete() {
    UserGroup group1 = new UserGroup(name: 'group1').save(flush: true, failOnError: true)
    UserGroup group2 = new UserGroup(name: 'group2').save(flush: true, failOnError: true)

    Invitation invitation = new Invitation(user:'user1', group: group1).save(flush: true, failOnError: true)

    assertEquals("Wrong number of groups.", 2, UserGroup.count())
    assertEquals("Wrong number of invitations.", 1, Invitation.count())

    fooService.deleteGroup group1

    assertEquals("Wrong number of groups.", 1, UserGroup.count())
    assertEquals("Wrong number of invitations.", 0, Invitation.count())
}

一些不相关的小注释 -默认情况下,属性不为 null,因此您可以删除 group(nullable: false) 约束。并且 Map 形式的 belongsTo 就像您定义了该名称的变量一样,因此您可以在 Invitation 中省略 UserGroup group

更新:

另一个侵入性较小的选项是在 UserGroup 中使用 before-delete 事件:

def beforeDelete() {
   Invitation.executeUpdate(
      'delete from Invitation where group=:group',
      [group: this])
}

I'm not sure if it's possible without the bidirectional relationship, but you can easily do it yourself in a transactional service method (to make sure the deletes all happen or none happen):

void deleteGroup(UserGroup group) {
   Invitation.executeUpdate(
       'delete from Invitation where group=:group',
       [group: group])
   group.delete(flush: true, failOnError: true)
}

and then your test becomes

def fooService

...

void testCascadingDelete() {
    UserGroup group1 = new UserGroup(name: 'group1').save(flush: true, failOnError: true)
    UserGroup group2 = new UserGroup(name: 'group2').save(flush: true, failOnError: true)

    Invitation invitation = new Invitation(user:'user1', group: group1).save(flush: true, failOnError: true)

    assertEquals("Wrong number of groups.", 2, UserGroup.count())
    assertEquals("Wrong number of invitations.", 1, Invitation.count())

    fooService.deleteGroup group1

    assertEquals("Wrong number of groups.", 1, UserGroup.count())
    assertEquals("Wrong number of invitations.", 0, Invitation.count())
}

A couple of minor unrelated notes - properties are not-null by default, so you can remove the group(nullable: false) constraint. And a belongsTo in Map form like you have defines a variable of that name, so you can omit UserGroup group in Invitation.

UPDATE:

Another less intrusive option is to use the before-delete event in UserGroup:

def beforeDelete() {
   Invitation.executeUpdate(
      'delete from Invitation where group=:group',
      [group: this])
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文