1

I have an Abstract Class which all DomainClasses are extending. This Abstracte Class looks like this:

abstract class DomainBase { 
    Date created = new Date(), modified = new Date()
    User createdBy, modifiedBy
    int dataStatus = 30 
}

My DomainClass User also extends the abstract Class and has multiple self-referencing relationship:

User principal

static hasMany = [employees: User, skills: UserSkill,...]
static mappedBy = [employees: 'none' ]

UserSkill:

class UserSkill extends DomainBase {

    String category
    String name 
    static belongsTo = [User]
    static hasMany = [users: User]
    static mappedBy = [users: 'skills']

    static mapping = {
        table 'skill'
        users column: 'skill_id', joinTable: 'user_skills' 
      }
}

With that i'm getting an MappingException:

nested exception is org.hibernate.MappingException: broken column mapping for: 
createdBy.skills of: de.streit.user.UserSkill

How do I mapp the classes correctly?

YAT
  • 456
  • 1
  • 4
  • 14

1 Answers1

2

Stepping away from Grails for a moment... you've got an object-oriented design problem. According to your design, DomainBase sits at the top of your hierarchy. Because of this DomainBase should not depend on its subclasses. Here's why:

According to the Liskov substitution principle, if class B extends from class A, then an instance of class B should be usable wherever an instance of class A is expected.

For example, if class Duck extends class Bird, I can honestly say a Duck is a Bird. My nose would not get any longer. If a method expects a Bird and I give it a Duck the method won't know the difference.

In your case, a User cannot be a DomainClass because a DomainClass has a user. Can a Bird have a Duck? Nope. A Bird should not know anything about Ducks. Animals aside, your class hierarchy violates this principle. But this can be fixed :)

Solution

As long as you're using Groovy 2.3 or grater, a Groovy trait can address your issue.

First, create a trait somewhere in grails-app/src/main/groovy/. It's best if you place it in the same Groovy (Java) package as your domain classes.

package xzy

trait Auditable { 
    Date created = new Date(), modified = new Date()
    User createdBy, modifiedBy
    int dataStatus = 30 
}

Then, have your domain classes implement the trait.

package xyz

class User implements Auditable {
     User principal

     static hasMany = [employees: User, skills: UserSkill,...]
     static mappedBy = [employees: 'none' ]
}

class UserSkill implements Auditable {

    String category
    String name 
    static belongsTo = [User]
    static hasMany = [users: User]
    static mappedBy = [users: 'skills']

    static mapping = {
        table 'skill'
        users column: 'skill_id', joinTable: 'user_skills' 
      }
}

This works because your domain classes will magically get the properties defined in the trait (created, createBy, and dataStatus) without baggage of inheritance. Plus, if a method expects an Audiable, you can pass it a User or UserSkill and the method wouldn't know the difference.

Watch this: Users and UserSkills are Auditable. Makes sense huh?

Community
  • 1
  • 1
Emmanuel Rosa
  • 9,697
  • 2
  • 14
  • 20