Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 358
Jörg Brenninkmeyer Avatar asked Jun 29 '10 14:06

Jörg Brenninkmeyer


People also ask

How do you save Hasmany in Grails?

You are doing user. addToReponse(q) instead it should be user. addToReponse(questionnaire) , if it's not a typo and the data is actually not being stored, then check by adding the failOnError parameter to save() method. Sometimes grails save() method fails silently, it should tell you if this is the case.

What is Grails domain class?

A domain class fulfills the M in the Model View Controller (MVC) pattern and represents a persistent entity that is mapped onto an underlying database table. In Grails a domain is a class that lives in the grails-app/domain directory.

What is Grails Gorm?

GORM is the data access toolkit used by Grails and provides a rich set of APIs for accessing relational and non-relational data including implementations for Hibernate (SQL), MongoDB, Neo4j, Cassandra, an in-memory ConcurrentHashMap for testing and an automatic GraphQL schema generator.


1 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

like image 189
proflux Avatar answered Sep 28 '22 06:09

proflux