When should hasMany be used for N:1 relationships in grails domain classes?

In grails, I can implement an N:1 relationship like this:

class Parent { hasMany = [children:Child] }
class Child  { belongsTo = [parent:Parent] }

Now (if addTo and removeFrom is always properly used) I can get a Parent's children via parent.children.

But I can also do it without hasMany:

class Parent { }
class Child  { belongsTo = [parent:Parent] }

Then I have to use Child.findAllByParent(parent) to get all children.

My question: Are there any important reasons why I should use hasMany if can query a parent's children in the second way as well?

I guess that it's sometimes easier (and perhaps faster if eager-fetched together with the parent?) to just refer to parent.children, but on the other hand this List can become rather long when there are several children. And what I don't like about hasMany either is that you always have to take care about the addTo or removeFrom or to clear the session after adding a new Child with a Parent so that grails does this automatically...

Is the answer that you should simply use hasMany if there are few children and don't use it if there are many (for performance reasons), or is there more behind it?

Answers


Using hasMany versus belongsTo is more related to the cascading behavior you want to specify when an update/delete occurs. In your second example, the cascading is set to ALL on the children side and NONE on the parent side. If you delete a child, nothing will happen on the parent. If you delete the parent, all the children will automatically be deleted.

In your first example cascading is set to ALL on the parent side, and SAVE-UPDATE on the child side. So now you can do something like:

parent.addToChildren(child1)
parent.addToChildren(child2)
parent.addToChildren(child3)
parent.save(flush:true)

And when you save the parent, all the children will be updated.

Touching on something you didn't ask, you could also presumably have something like:

class Parent { hasMany = [children:Child] }
class Child  { Parent parent }

If you define the relationship from Child to Parent in this way, you will need to manually manage the Child objects that reference the parent when you delete the parent*. (*this corrects a previous statement that proved to be inaccurate)

So, the hasMany/belongsTo has two main considerations:

  1. What kind of cascading strategy do you want to execute on updates/deletes
  2. How are you most likely going to access the data, if you expect to need to retrieve a set of children for a parent, having a parent.getChildren() method is pretty convenient.

UPDATE:

I also want to clarify, GORM will not eager-fetch when you use hasMany; by default GORM uses a lazy fetch strategy so it won't get the children until attempt to access parent.children

If you want an association to be eagerly fetched by default, you can specify the appropriate mapping:

class Parent { 
  hasMany = [children:Child]
  static mapping = {
    children lazy:false
  }
}

Finally, you mentioned that you don't like that you have to worry about the addTo/removeFrom on the hasMany side. You shouldn't have to do this if you save with flush:true.

def parent = Parent.get(id)
def child = new Child(name:'child1', parent:parent)
if(child.save(flush:true)) {
  // both sides of the relationship should be good now
} 

EDIT: fixed reversed order of child/parent cascade defaults and corrected misconception regarding how gorm handled relationships without belongsTo


Great question, and the currently accepted answer is good. There is one other important performance consideration, which is what happens when you add and save a new child. In your first example, Grails by default has to load the entire list of children from the database before inserting the new one into the Set, to guarantee uniqueness. In the second case, it does not, which leads to much better performance. You can get around this behaviour in your first example by defining the children as a 'Collection' as per http://grails.org/doc/latest/guide/single.html#sets,ListsAndMaps


Need Your Help

PHP: Can CURL follow meta redirects

php redirect curl meta-tags

CURL can follow header redirects with the use of CURLOPT_FOLLOWLOCATION but is it possible to follow meta refresh redirects?

Automatically adjust the height of WebControl

c# wpf wpf-controls windows-phone webbrowser-control

I have a web control with some properties set.Below is the code.