0
votes

I'm trying to create a bi-directional many-to-one relationship in Grails with NON-cascading deletes in both directions. My domain looks like:

class Asset {
  static hasMany = [ hosts: Host ]

  static mapping = {
      hosts cascade: 'save-update'
  }
}

class Host {
  Asset asset
}

This works fine when deleting a Host (the Asset remains), but when I try to delete the Asset, I get an integrity constraint error from the DB. I've tried changing the Host class definition to use a belongsTo, but can't get this to work. Any help would be great! Thanks!

3
What database are you using?James Kleeh
Try adding nullable: true on the asset property of Host?James Kleeh
I'm using hsqldb on my local dev. I have it set to nullable:true and just can't figure out why I'm still getting the error..Nisrak
Have you try remove "hosts cascade: 'save-update'" and add belongsTo?Xilang
I tried adding belongsTo[asset:Asset] on Host and remove the cascade definition in Asset but I still get the error message when trying to delete the Asset.Nisrak

3 Answers

0
votes

You have parent (Asset) and children (Host) classes with the following rules/properties:

  1. Parent has children
  2. Children can't exists without parent (bi-directional)
  3. Parent can't remove children

Your dilemma is you want to be able to remove Parent - but you would be breaking rule #2. Even with a mapping table, I don't think you can do what you're asking. I've never had this use case, and without re-designing too much of your current model, my guess is that you have to:

  1. as @JamesKleeh pointed out - allow nullable on Host.asset - basically Children CAN exists without parent
  2. or allow for parent to remove children (i.e, cascade delete)
0
votes

This is a many-to-many relationship, which in GORM would actually work the way you want: you can add hosts to the Asset owner and safely delete the Asset without affecting the hosts.

0
votes

I ended up finding a solution by writing my own delete() action for the Asset controller that deletes all the references to the Asset from all hosts before deleting the Asset itself:

def delete() {
    def assetInstance = Asset.get(params.id)        
    assetInstance.hosts.each { theHost ->
        theHost.asset = null
        theHost.save()
    }
    if(!assetInstance.hasErrors() && assetInstance.delete()) {
        redirect(action: "list")
    }
    else {
        redirect(url: "/asset/show?id=${assetInstance.id}")
    }
}

This eliminates the error and also prevents children (Hosts) from being deleted when the parent (Asset) is deleted.