Search code examples
grailsgroovygrails-orm

Grails 2.5, beforeDelete cannot access one-to-many relationship


Using Grails 2.5.6 here. I'm trying to access a Set of Strings off of my domain class in the beforeDelete GORM event. I'm seeing the deletes for this set getting issued in the database log before even getting to my breakpoint in the beforeDelete.

I'm getting a NullPointerException on my println(strings) below in my domain class.

My test domain class looks like

class DeleteTest {
    Integer id
    Set<String> stringSet
    String prop1
    String prop2

    static hasMany = [stringSet: String]

    static constraints = {
        prop1(maxSize: 20)
        prop2(maxSize: 20)
    }

    static mapping = {
        stringSet(joinTable: [column: 'delete_test_string_set', length: 15])
    }

    def beforeDelete() {
        withNewSession {
            Set<String> strings = this."stringSet"
            println(strings)
        }
    }
}

And I've made a test controller like this.

class DeleteTestController {

    def create() {
        DeleteTest test = null
        DeleteTest.withTransaction {
            test = new DeleteTest(
                    prop1: 'Test',
                    prop2: 'another test',
                    stringSet: ['str1', 'str2', 'str3']
            ).save()
        }

        render (test as JSON)
    }

    def delete() {
        DeleteTest test = DeleteTest.findByProp1('Test')
        DeleteTest.withTransaction {
            test.delete()
        }

        render(test as JSON)
    }
}

How can I get my stringSet in the beforeDelete event?


Solution

  • One easy way is to make sure to load stringSet before calling the delete. However, there are clearly some odd behaviors going on here and I'll describe what I have found so far.

    Simple Answer

    def delete() {
        DeleteTest test = DeleteTest.findByProp1('Test')
        test.stringSet?.size() // <-- force load here
        DeleteTest.withTransaction {
            test.delete()
        }
        render(test as JSON)
    }
    

    Other Considerations

    I tried making stringSet eager loaded. This did not work as expected and in the beforeDelete code it would often be a single value or blank.

    I also tried making StringSet a Set where I defined a single GORM object MyString containing the value. This did work (though I had to make it eagerly fetched), but I did not consider this to be a valid solution for your case since I assume you have data already and can't just replace it.

    Based on some debug digging, I'm guessing (but it really is just a guess) that the collection is deleted before the beforeDelete event fires, and so it can't be lazily loaded at that point even in a new transaction. I would expect that someone else could weigh in on whether that's right or not, but grails 2 expertise is getting harder to find these days.