Search code examples
grailsgrails-orm

Grails/Gorm: Inheritance and foreign keys


Given Domain Classes like these:

class A {   // in reality this is a basic User class that is required in multiple projects
}

class B extends A { // in reality B is a "patient"-kind of user. 
    static hasMany = [c: C]
}

// c/d is stuff like "MedicationPrescription", so basically data only relevant to the patient. However the system needs to realize that Patients are Users, as the User base class is used for spring security logins and in general has a lot of the basic data a person just has. (Name, etc.)
class C {
    static belongsTo = [b: B, a: A, d: D]
}

class D {
}

I get this error:

org.hibernate.MappingException: Foreign key (FK_pwu2w72ul5a5213husrv3onr3:c [])) must have same number of columns as the referenced primary key (a [id])
        at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:110)
        at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:93)
        at org.hibernate.cfg.Configuration.secondPassCompileForeignKeys(Configuration.java:1818)
        at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1741)
        at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1426)
        at org.grails.orm.hibernate.cfg.HibernateMappingContextConfiguration.secondPassCompile(HibernateMappingContextConfiguration.java:287)
        at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
        at org.grails.orm.hibernate.cfg.HibernateMappingContextConfiguration.buildSessionFactory(HibernateMappingContextConfiguration.java:196)
        at org.grails.orm.hibernate.HibernateMappingContextSessionFactoryBean.doBuildSessionFactory(HibernateMappingContextSessionFactoryBean.java:476)
        at org.grails.orm.hibernate.HibernateMappingContextSessionFactoryBean.buildSessionFactory(HibernateMappingContextSessionFactoryBean.java:470)
        at org.grails.orm.hibernate.HibernateMappingContextSessionFactoryBean.afterPropertiesSet(HibernateMappingContextSessionFactoryBean.java:93)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
        ... 41 common frames omitted

The error goes away if I remove the inheritance between A and B, but I havee this constellation in my Domain. I don't understand why Gorm does what it does, from the error message it seems to think the list of attributes in the foreign key is empty?

Additionally I cannot even mention classes B, C or D in class A, as class A is part of a plugin that just doesn't know about those classes.

EDIT: I might just do away with the inheritance and use composition instead like this:

class B {
  A a
  static hasMany = [c: C]
}

That doesn't blow up on startup at least, but still: Why?


Solution

  • You can take care of your domain class constellation like this:

    class User { }
    
    class Patient extends User {
        static hasMany = [prescriptions: MedicationPrescription, stuff: OtherStuff]
    }
    
    class MedicationPrescription {
        static belongsTo = [patient: Patient]
    }
    
    class OtherStuff {
        static belongsTo = [patient: Patient]
    }
    

    This creates a bi-directional one-to-many association between Patient and MedicationPrescription and also between Patient and OtherStuff. Notice that each belongsTo has a corresponding hasMany.

    As you've already discovered, you can also use composition.