The Scenario
I have a need to represent an object in two different contexts. One context should not persist and the other should. The persistent objects are actual data pulled from another system. The non-persistent objects represent parts of a product definition. The two will be compared, and I'm not interested in storing the definition data. The persisting object needs to have additional information stored with it.
The Implementation
In order to accomplish this, I decided that the most logical thing to do would be to create the base class in the src/groovy folder to avoid grails/hibernate wanting to persist it as a domain class.
class Resource{
String name
Date lastModified
}
The domain class that I want to persist looks like the following.
class OwnedResource extends Resource{
Date dateCreated, lastUpdated
Owner owner
static belongsTo = [owner: Owner]
static mapping = {
//I assumed I would need this so that grails would not expect
//to store the object in the base class table that doesn't exist
tablePerHierarchy true
table 'owned_resource'
}
}
Lastly we then have the Owner class which hasMany OwnedResources.
class Owner{
Date dateCreated, lastUpdated
String name
static hasMany = [
resources: OwnedResource
]
}
The Problem
When I run-app, I end up getting a friendly Hibernate exception:
2011-10-24 20:32:01,307 [main] ERROR context.GrailsContextLoader - Error executing bootstraps:
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: Association references unmapped class: OwnedResource
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: Association references unmapped class: OwnedResource
at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:212)
at grails.web.container.EmbeddableServer$start.call(Unknown Source)
at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy:158)
at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy)
at _GrailsSettings_groovy$_run_closure10.doCall(_GrailsSettings_groovy:280)
at _GrailsSettings_groovy$_run_closure10.call(_GrailsSettings_groovy)
at _GrailsRun_groovy$_run_closure5.doCall(_GrailsRun_groovy:149)
at _GrailsRun_groovy$_run_closure5.call(_GrailsRun_groovy)
at _GrailsRun_groovy.runInline(_GrailsRun_groovy:116)
at _GrailsRun_groovy.this$4$runInline(_GrailsRun_groovy)
at _GrailsRun_groovy$_run_closure1.doCall(_GrailsRun_groovy:59)
at RunApp$_run_closure1.doCall(RunApp.groovy:33)
at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:427)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:415)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.executeTargets(Gant.groovy:590)
at gant.Gant.executeTargets(Gant.groovy:589)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: 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: Association references unmapped class: OwnedResource
... 24 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Invocation of init method failed; nested exception is org.hibernate.MappingException: Association references unmapped class: OwnedResource
... 24 more
Caused by: org.hibernate.MappingException: Association references unmapped class: OwnedResource
... 24 more
Perhaps my implementation is bad practice, since after much googling and searching of StackOverflow, I have yet to encounter anyone facing this same issue or attempting a similar implementation. Most people are attempting to use abstract classes, which does work. I want the class Resource to be concrete because I need to instantiate it. The answer may simply be that grails doesn't allow this functionality, but I'd like to hear a definitive answer and any possible work arounds. I'm leaning toward having to duplicate the class instead of using inheritance.
What am I doing wrong and why is this error occurring? Can this implementation be done in grails?
Use abstract base class Resource and then add a 3rd class that extends Resource that is not a domain object but a simple utility class that you can use for other things and not worry about persistence. Good luck... Dmitry.