Grails: Lessons Learned
Here are some lessons learned concerning lazy vs eager fetching and how to delete a child object in a One-to-many relationship (unfortunately it's not super obvious).
First Lesson: Don't set eager fetching globally
I by no means am a grails expert, but based on my experience don't set the eager fetching in your Domain as the example shows (unless you have a very good reason and understand the consequences). Lazy vs Eager fetching is well described in the grails documentation, so I won't repeat it, but anyone using One-to-many relationships needs to know the differences.
The default behavior in grails is lazy fetching, which results in n+1 queries. In some cases this might be ideal, in others it may not. When it's not you have a couple of choices. The example in the grails documentation sets a fetchMode property on the Domain. This sets it globally and every time the Domain is accessed, grails is going to load all it's many relationships. The path I recommend is to specify the fetch mode when retrieving the data. For example, the list() method has a parameter called fetch and can be used like this: Book.list(fetch: [authors: "eager"]). This gives you the most flexibility by not specifying the fetch mode globally, but allowing you to fetch eagerly when necessary.
Second Lesson: Use Hibernate Events to help remove associations
Like myself, you might actually have a One-to-many relationship where you need to delete a child. Unfortunately this use case isn't documented very well and it actually took me a little bit to figure out.
So lets say you have the following two domains
And you save the followingclass Parent {
static hasMany = [kids: Kid]
String name
}
class Kid {
static belongsTo = [parent: Parent]
String name
}
Now Ayden turns 18 and going off to college and you need to remove him. You might think this would work:new Parent(name: "James").addToKids(name: "Ayden").save()
But it doesn't because the parent James still has a reference to the kid Ayden in the kids list (parent.kids). So you have to do the following:Kid.findByName("Ayden").delete()
Why that isn't the default behavior in grails I don't know, but to prevent you from repeating code everywhere you can use Hibernate events. In the Kid Domain add the following beforeDelete property:def kid = Kid.findByName("Ayden")
kid.parent.removeFromKids(kid)
kid.delete()
And now when you want to remove a Kid, all you need to do is call kid.delete(). The hibernate events are interesting. By default grails supports 4 events: beforeInsert, beforeUpdate, beforeDelete, onLoad. However, there is a recent plugin called Hibernate Events Plugin that adds 7 more events: beforeLoad, afterLoad, beforeSave, afterSave, afterInsert, afterUpdate, afterDelete.class Kid {
static belongsTo = [parent: Parent]
def beforeDelete = {
parent.removeFromKids(this)
}
String name
}