Showing posts with label lessons. Show all posts
Showing posts with label lessons. Show all posts

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

Read more »