Sunday, January 29, 2017

Grails Lessons Learned

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 its not super obvious).

First Lesson: Dont set eager fetching globally
I by no means am a grails expert, but based on my experience dont 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 wont 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 its 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 its 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 isnt documented very well and it actually took me a little bit to figure out.

So lets say you have the following two domains
class Parent {
static hasMany = [kids: Kid]
String name
}

class Kid {
static belongsTo = [parent: Parent]
String name
}

And you save the following
new Parent(name: "James").addToKids(name: "Ayden").save()
Now Ayden turns 18 and going off to college and you need to remove him. You might think this would work:
Kid.findByName("Ayden").delete()
But it doesnt 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:
def kid = Kid.findByName("Ayden")
kid.parent.removeFromKids(kid)
kid.delete()

Why that isnt the default behavior in grails I dont know, but to prevent you from repeating code everywhere you can use Hibernate events. In the Kid Domain add the following beforeDelete property:
class Kid {
static belongsTo = [parent: Parent]
def beforeDelete = {
parent.removeFromKids(this)
}
String name
}

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.

Available link for download