I have a domain class in grails that should only be created once with the same name. To ensure that I have a static method getColor and private constructors that looks as follows:
class Color {
String name
static hasMany = [moods: Mood]
// not accessible
private Color() {}
// not accessible because getColor should be used
private Color(String name) {
this.name = name
}
static getColor(String name) {
def color = Color.findByName(name.toLowerCase())
color ? color : new Color(name).save(flush:true)
}
def beforeValidate() {
name = name.toLowerCase();
}
}
to ensure that object of Color are only created by using the static getColor method I wanted to make the constructor private. It works so far, that I can create objects of Color. But when I use this instances to create an object of Object Mood
class Mood {
static belongsTo = [color:Color]
}
def color = Color.getColor('verylightgreen')
def mood = new Mood(color: color)
I get an exception:
error initializing the application: Could not instantiate bean class [de.tobi.app.Color]: Is the constructor accessible?
This exception is thrown by
def mood = new Mood(color: color)
So why does creation of Mood need access to the constructor of Color. I already passed the object.. And in general, whats the best way in groovy/grails to hide the contructor of a domain class to controll how objects are created. Especially the usage of the map controller should be disabled too.
Regarding the exception:
The exception is occurring due to the map constructor. With normal groovy classes, this is not an issue, but grails registers domain classes as prototype beans. It then overrides the constructor in the metaclass to use the bean creation and autowiring mechanism to get an instance. Something in the map constructor and auto-wiring is causing an empty color bean to be created prior to being set by the map.
If you change the code to:
Color c = Color.getColor('red')
Mood m = new Mood()
m.color = c
m.save()
The exception should disappear.
You may consider filing a JIRA issue for this particular use case, but I don't know whether or not the grails team would consider this a bug or a design decision. It is certainly not documented anywhere.
Regarding the design:
Without knowing much more about your model, I agree with dmahapatro about shifting the responsibility for data integrity onto your database and GORM constraints. That's what they're for.
Circumventing this causes unusual use patterns in the code such as knowing to use Color.getColor
as opposed to normal domain class instantiation.
The ideology behind a convention over configuration framework like Grails is to adhere to the conventions as much as possible, such that anyone familiar with the conventions can step in and know immediately what is going on.