Reputation: 1467
If I create an abstract domain class such as this:
abstract class DomainBase {
LocalDateTime created = LocalDateTime.now()
LocalDateTime updated
static constraints = {
created nullable: false
updated nullable: true
}
static mapping = {
tablePerConcreteClass true
created column: 'CREATED'
updated column: 'UPDATED'
}
def beforeUpdate() {
this.updated = LocalDateTime.now()
}
}
Then i can extend other domain classes with this and they will inherit everything (the properties, constraints, mappings and interceptors), and the generated database contains no concrete DOMAIN_BASE
table. With that everything works as expected.
However, if I create another abstract class which extends DomainBase, for example:
abstract class EntityBase extends DomainBase {
User createdBy
Boolean active = true
static constraints = {
createdBy nullable: false
active nullable: true
}
static mapping = {
tablePerConcreteClass true
createdBy column: 'CREATED_BY_ID'
active column: 'ACTIVE'
}
}
Then the generated database will have a concrete ENTITY_BASE
table. And that is the problem.
Proposed solution from what I could gather was to have the base classes be ordinary POGO's and not domain objects, however then the mapping is not inherited and I'm much too lazy to copy paste the mapping into every single domain class I create.
Also if I try to make these domain objects to be traits I can't even run the app due to it breaking with an NPE during compilation.
Is there a simple and elegant way to solve this?
Upvotes: 2
Views: 467
Reputation: 1467
My current solution with which I'm somewhat satisfied with is an adaptation of This SO answer only wrapped into an "Utility" closure and looks like this:
/**
* Domain related utilities
*/
class DomainUtil {
private DomainUtil() {}
/**
* Utility for inheriting mapping definitions from non-domain classes (classes defined in src/groovy)<br/>
* Adapted from <a href="https://stackoverflow.com/a/18339655/1182835">https://stackoverflow.com/a/18339655/1182835</a>
* @param source - source class from which to inherit the mappings
* @param destination - closure to which the mappings will be added
*/
static def inheritDomainMappingFrom = { source, destination ->
def copyMapping = source.mapping.clone()
copyMapping.delegate = destination
copyMapping.call()
}
}
And It is used like this:
Given some abstract class Foo
which has a mapping closure and is stored inside src/groovy
so it's not considered a domain class:
abstract class Foo {
LocalDate prop1
Boolean prop2
static mapping = {
prop1 column: "PROP1_DATE", type: PersistentLocalDate
prop2 column: "IS_PROP2"
}
}
And a domain class Bar
which inherits Foo
:
class Bar extends Foo {
Integer prop3
String prop4
String prop5
static mapping = {
DomainUtil.inheritDomainMappingFrom(Foo, delegate)
prop3 column: 'PROP3'
prop4 column: 'PROP4'
prop5 column: 'PROP5'
}
}
Using DomainUtil.inheritDomainMappingFrom(Foo, delegate)
my Bar
domain class will inherit all the mapping definitions from Foo
abstract base class. This, for me, is an adequate compromise since it's easier to write and for someone joining or maintaining the project to understand.
Extending Scaffolding Templates as suggested by Yannic-AT is also a possible solution however it contains a few drawbacks which put me off:
However it is quite useful for stuff adding the @ToString
annotation to all my domain classes.
Upvotes: 0
Reputation: 466
As far as i know you can't inherit the mapping in Grails 2.4.x, it think this in 2.5.0 the same...
As a workaround you can use and change the Scaffolding Template for the domain classes.
Upvotes: 1