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?
Abstract Classes support both roles in the Inheritance relationship. Abstract Classes support only one role in Realisation relationship.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With