Reputation: 6131
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
Upvotes: 3
Views: 1254
Reputation: 75671
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])
}
Upvotes: 5