Question

The following is not working for me when using abstract (or non-abstract for that matter) inheritance in Grails.

Very quickly, my inheritance is as follows:

abstract BaseClass               { ... }
SomeClass      extends BaseClass { ... }
SomeOtherClass extends BaseClass { ... }

And then in another domain object:

ThirdClass {
    ...
    BaseClass baseProperty
    ...
}

But now, when I try to set that property to either a SomeClass or SomeOtherClass instance, Grails compains:

ERROR util.JDBCExceptionReporter - Cannot add or update a child row: a foreign key constraint fails ...

Is this not possible?


I have also tried having the base class not be abstract, and also tried casting the SomeClass or SomeOtherClass instances to BaseClass. They generate the same error.


UPDATE

I just checked. It works for the first sub-class that I add. But as soon as I try to add the other sub-class it fails.

In other words:

def prop1 = new ThirdClass(baseProperty: instanceOfSomeClass).save()

works fine. But when I then try and do:

def prop2 = new ThridClass(baseProperty: instanceOfSomeOtherClass).save()

it fails.


UPDATE 2

Further investigation shows that something goes wrong during the table creation process. It correctly adds two foreign keys to the ThirdClass table, but the keys incorrectly references:

CONSTRAINT `...` FOREIGN KEY (`some_id`) REFERENCES `base_class` (`id`),
CONSTRAINT `...` FOREIGN KEY (`some_id`) REFERENCES `some_class` (`id`)

Don't know why it's choosing the base class and one of the sub-classes? I have tried cleaning etc.

Was it helpful?

Solution

First of all, create your BaseClass outside domain structure. It must be an external class, put it on script folder, source folder.

package com.example.model

/**
 * @author Inocencio
 */
class BaseClass {

    Date createdAt = new Date()

}

Now, create a regular domain class and extend it.

package com.example.domain

import com.example.model.BaseClass

class SomeClass extends BaseClass {

    String name

    static constraints = {
        name(nullable: false)
    }

}

As you can see, once you persist SomeClass a createdAt field is filled and saved as well. Check the test class out:

@TestFor(SomeClass)
class SomeClassTests {

    void testSomething() {
        def some = new SomeClass()
        some.name = "Hello There"
        some.save()

        //find it
        def someFind = SomeClass.list()[0]

        assert someFind

        assertTrue someFind.createdAt != null

//        println "Date: $someFind.createdAt"
//        println "Name: $someFind.name"
    }
}

I hope it can be helpful.

OTHER TIPS

I have just created class structure as yours (Grails 2.1.0) and there is no problem. It works when mocked and unit-tested. The same when scaffolded and SomeClass and ThirdClass instances saved from forms. Try clean your DB, especially if you haven't used 'create-drop' mode. Maybe there is some old constraint left. Last thing, you haven't specified when the error occurs - on save (create or update)? It's rather not probable to get JDBC exception on property set, is it? I don't remember for sure but it's possible that simple property isn't cascaded by default then try to save SomeClass instance before saving the ThirdClass instance. Also you can auto-cascade instead of declaring simple property by use hasOne relation like:

class ThirdClass {
    ...
    static hasOne = [baseProperty:BaseClass]
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top