I have a class, DigitalObject which is basically a container for a url and the date that it was last modified.
class DigitalObject {
String url
Date lastUpdated
}
I have two classes that use DigitalObjects to reference different urls related to them
class Video {
DigitalObject englishVersion
DigitalObject frenchVersion
}
class Image {
DigitalObject thumbnailImage
DigitalObject fullSizeImage
}
I'm having a ton of trouble getting the mapping correct.
I want to be able to delete a DigitalObject and have GORM take care of removing it from it's parent.
If I leave it the way it is above when I try and delete the digitalObject I get a foreign key constraint violation.
If I add hasOne mapping on the parent side I get an error saying to specify the other side of the relationship, but I am unclear on where to do that as a DigitalObject can belong to either an Image or Video. Like such.
class Video {
static hasOne = [englishVersion: DigitalObject, frenchVersion: DigitalObject]
}
Adding belongsTo in the child class results in the same foreign key constraint error. Like such.
class DigitalObject {
String url
Date lastUpdated
static belongsTo = [Image, Video]
}
Am I going to have to just give up and handle removing it from its parent manually?
Thanks in advance for any assistance, and sorry if this is answered else ware I was unable to come up with a search query that provides results that deal with a case such as mine although I'm sure this can't be an uncommon use case.
EDIT 24 Jan 2014
Attempting Andrew's suggestion, my actual domain classes are a big more complex than the examples I used therefore my my beforeDelete() ends up like what follows, which is very ugly and could potentially be a pain to maintain.
def beforeDelete() {
DigitalObject.withNewSession {
def imageThumbnailImages = Image.findAllByThumbnailImage(this)
if (imageThumbnailImages) {
imageThumbnailImages.each { image ->
image.thumbnailImage = null
image.save(flush: true)
}
}
def imagePreviewImages = Image.findAllByPreviewImage(this)
if (imagePreviewImages) {
imagePreviewImages.each { image ->
image.previewImage = null
image.save(flush: true)
}
}
def imageFullImages = Image.findAllByFullImage(this)
if (imageFullImages) {
imageFullImages.each { image ->
image.fullImage = null
image.save(flush: true)
}
}
def videoThumbnailImages = Video.findAllByThumbnailImage(this)
if (videoThumbnailImages) {
videoThumbnailImages.each { image ->
image.thumbnailImage = null
image.save(flush: true)
}
}
def videoPreviewImages = Video.findAllByPreviewImage(this)
if (videoPreviewImages) {
videoPreviewImages.each { image ->
image.previewImage = null
image.save(flush: true)
}
}
def videoFullVideosEng = Video.findAllByFullVideoEng(this)
if (videoFullVideosEng) {
videoFullVideosEng.each { video ->
video.fullVideoEng = null
video.save(flush: true)
}
}
def videoFullVideosFra = Video.findAllByFullVideoFra(this)
if (videoFullVideosFra) {
videoFullVideosFra.each { video ->
video.fullVideoFra = null
video.save(flush: true)
}
}
def captionsEngVideos = Video.findAllByCaptionsEng(this)
if (captionsEngVideos) {
captionsEngVideos.each { videos ->
videos.captionsEng = null
videos.save(flush: true)
}
}
def captionsFraVideos = Video.findAllByCaptionsFra(this)
if (captionsFraVideos) {
captionsFraVideos.each { video ->
video.captionsFra = null
video.save(flush: true)
}
}
def signLanguageEngVideos = Video.findAllBySignLanguageEng(this)
if (signLanguageEngVideos) {
signLanguageEngVideos.each { video ->
video.signLanguageEng = null
video.save(flush: true)
}
}
def signLanguageFraVideos = Video.findAllBySignLanguageEng(this)
if (signLanguageFraVideos) {
signLanguageFraVideos.each { video ->
video.signLanguageFra = null
video.save(flush: true)
}
}
}
This is ugly and probably not the best method for doing this but as I could not get the other methods suggested to work reliably, and due to the fact that I wanted to make the minimum changes as possible to the domain classes as my project is not the only one that uses them.
I added the following method to my DigitalObjectController.
def deleteFromMediaAsset(Long id, String parentClass, String parentProperty, String parentId) {
def digitalObjectInstance = DigitalObject.get(id)
if (!digitalObjectInstance) {
flash.message = message(code: 'default.not.found.message', args: [
message(code: 'digitalObject.label', default: 'DigitalObject'),
id
])
redirect(controller: parentClass, action: 'edit', id: parentId)
return
}
try {
def classOfParent = grailsApplication.domainClasses.find {
it.clazz.simpleName == parentClass.capitalize()
}.clazz
def parentInstance = classOfParent.get(parentId)
parentInstance.setProperty(parentProperty, null)
digitalObjectInstance.delete(flush: true)
flash.message = message(code: 'default.deleted.message', args: [
message(code: 'digitalObject.label', default: 'DigitalObject'),
id
])
redirect(controller: parentClass, action: 'edit', id: parentId)
}
catch (DataIntegrityViolationException e) {
flash.message = message(code: 'default.not.deleted.message', args: [
message(code: 'digitalObject.label', default: 'DigitalObject'),
id
])
redirect(controller: parentClass, action: 'edit', id: parentId)
}
}
Basically I pass the parent Image or Video class and ID along with the attribute to which the DigitalObject refers to and the ID of the DigialObject I wish to delete. I set the property to null on the parent object and then I delete the DigitalObject.
Ugly but it works for now, wish me luck.