Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstract Classes in GORM Relationships

Tags:

Grails GORM does not persist abstract domain classes to the database, causing a break in polymorphic relationships. For example:

abstract class User {
    String email
    String password
    static constraints = {
        email(blank:false, nullable:false,email:true)
        password(blank:false, password:true)
    }

    static hasMany = [membership:GroupMembership]
}

class RegularEmployee extends User {}

class Manager extends User {
    Workgroup managedGroup
}

class Document {
    String name
    String description
    int fileSize
    String fileExtension
    User owner
    Date creationTime
    Date lastModifiedTime
    DocumentData myData
    boolean isCheckedOut
    enum Sensitivity {LOW,MEDIUM,HIGH}
    def documentImportance = Sensitivity.LOW

    static constraints = {
        name(nullable:false, blank:false)
        description(nullable:false, blank:false)
        fileSize(nullable:false)
        fileExtension(nullable:false)
        owner(nullable:false)
        myData(nullable:false)
    }
}

causes

Caused by: org.hibernate.MappingException: An association from the table document refers to an unmapped class: User ... 25 more 2009-11-11 23:52:58,933 [main] ERROR mortbay.log - Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageSource': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Invocation of init method failed; nested exception is org.hibernate.MappingException: An association from the table document refers to an unmapped class: User: org.hibernate.MappingException: An association from the table document refers to an unmapped class: User

But in this scenario, I want the polymorphic effects of allowing any user to own a document, while forcing every user of the system to fit into one of the defined roles. Hence, User should not be directly instantiated and is made abstract.

I don't want to use an enum for roles in a non-abstract User class, because I want to be able to add extra properties to the different roles, which may not make sense in certain contexts (I don't wanna have a single User with role set to RegularEmployee that somehow gets a not null managedGroup).

Is this a bug in Grails? Am I missing something?

like image 486
Visionary Software Solutions Avatar asked Nov 12 '09 07:11

Visionary Software Solutions


People also ask

Do abstract classes imply an inheritance relationship?

Abstract Classes support both roles in the Inheritance relationship. Abstract Classes support only one role in Realisation relationship.

Can you inherit an abstract class?

An abstract class cannot be inherited by structures. It can contain constructors or destructors. It can implement functions with non-Abstract methods. It cannot support multiple inheritances.

What is Grails Gorm?

GORM is the data access toolkit used by Grails and provides a rich set of APIs for accessing relational and non-relational data including implementations for Hibernate (SQL), MongoDB, Neo4j, Cassandra, an in-memory ConcurrentHashMap for testing and an automatic GraphQL schema generator.

What is Grails domain class?

A domain class fulfills the M in the Model View Controller (MVC) pattern and represents a persistent entity that is mapped onto an underlying database table. In Grails a domain is a class that lives in the grails-app/domain directory.


2 Answers

We were testing out the grails inheritance heirarchy the other day at work to look at polymorphism. We found the following scenarios:

Abstract Superclass - Subclasses inherit the behavior of the parent but the parent cannot be used to reference a subclass you want to store in the database.

Superclass with tablePerHeirarchy false - Subclasses store the parent's fields in the parent's table, polymorphism works as expected.

Empty Superclass with tablePerHeirarchy false - Subclasses store all of their own data in their table, polymorphism works as expected.

So in your case, if you made were to remove the abstract keyword from the user class everything would work as expected. The only downside is all of the User fields are stored in the User table leaving the RegularEmployee table with only the id and version columns and the Manager table having only a reference to a Workgroup row.

like image 35
Blacktiger Avatar answered Oct 11 '22 17:10

Blacktiger


You might like to view the domain models for the Shiro, Nimble (uses Shiro), and/or Spring Security plugins. They create a concrete User domain and a concrete Role domain. Shiro in particular creates a UserRole domain for the many-to-many mapping.

Then on your Role domain, you can add whatever properties you want. If necessary, you can create a separate domain to allow for arbitrary properties like so:

class Role {
    //some properties
    static hasMany = [roleProperties:RoleProperty, ...]
}

class RoleProperty {
    String name
    String value
    static belongsTo = [role:Role]
}

I don't think you'll get what you're looking for in your current domain mapping though.

like image 151
Matt Lachman Avatar answered Oct 11 '22 17:10

Matt Lachman