Showing posts with label relationship. Show all posts
Showing posts with label relationship. Show all posts

Monday, May 31, 2010

Relationship Advice : Grails One-To-Many

If you are here investigating bigamy.... you have come to the wrong place.... We are actually going to have a quick once over on One-To-Many relationships in Grails and GORM (Let's abbreviate to OTM for the sake of my fingers). Here's what we'll cover:

  • Starting a Relationship
  • Living and Working With Your Relationship
  • Finding out more about your Relationship (or querying :) )
  • What if you don't belongTo someone?

Starting a Relationship :

First of all, what is an OTM? Let's let the code do the talking:

class Parent{
static hasMany = [children:Child]
String name
}

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


Here is a very simple domain model that will serve as a nice concise example. We have a Parent domain that can 'haveMany' children... in turn these children belongTo a specific parent. This is quite a 'tightly coupled' OTM. There can be a few nuances that catch people out here, which of course we will cover... (we will take a brief look at the "loosely coupled" OTM too).

The definition of the hasMany in the Parent class is actually a Map, and it should read like [collectionName:TypeContainedInCollection], so the parent has a collection with the name 'children', that will contain instances of the Child domain class.

Similarly in the Child class, the 'belongsTo' property is also a Map, [instanceOfOwningClass:typeOfOwningClass]. The 'belongsTo' property forms what I referred to earlier as a 'tightly coupled' OTM. This basically entails that the children instances are 'owned' by their 'parent', this in turn has some interesting side effects that we will cover now.

Living and Working With Your Relationship :

OK let's create a parent with some children :

def parent = new Parent(name:'Dad')

//add a new child instance
parent.addToChildren(new Child(name:'son'))

//add another child instance using a map
parent.addToChildren([name:'daughter'])

//save the parent
parent.save(flush:true,failOnError:true)

To add a Child to a Parent we use the 'addTo' method that is provided by Grails, it is basically addTo+Name of Association. When we use these methods, GORM will wire up our domain classes and because children have a 'belongsTo' property set, calling save() on the parent will also validate and save the children... sweetness :)

OK now... what if we need to remove some children from our relationship.... easy....

//retrieve our parent
def parent = Parent.findByName('Dad')

//retrieve the child instance
def child = Child.findByName('son')

//remove the child
parent.removeFromChildren(child)

parent.save()

Oh.... wait.... NOT EASY!!! If we run the above code we get the following Exception:

"not-null property references a null or transient value: Child.parent"

Yeeeeeeeeesh.... not pretty... The knee jerk reaction here might to add a nullable constraint for the parent property in the Child class... but this would be wrong. The relationship we defined has a 'belongsTo' so in this case, if we remove a child.... we simply want it to be deleted automatically at the same time. To achieve this, our Parent domain must look like:


class Parent{
static hasMany = [children:Child]
String name

static mapping = {
children cascade: "all-delete-orphan"
}
}

Now if we re-run the previous code, the child instance will be removed from our relationship and also deleted!


Finding out more about your Relationship :

If we have a Parent instance, it is rather easy to query for our children.... we simply access them as a property:

def parent = Parent.findByName(name:'Dad', [lazy:[children:false]])
parent.children.each{//do something}

However, things are not so clear cut when coming from the other direction. Suppose we have the name of a child and we want to find all the parents that have children with that name, so basically querying the relationship from the many side:

def name = 'son'

//create criteria so we can perform a criteria query
def c = Parent.createCriteria()

//perform a listDistinct as querying relationships can result in duplications
def parents = c.listDistinct{
createAlias("children", "c")
eq("c.name", name)
}

Thanks to Robert Fletcher for blogging on this (link to follow)....

What if you don't belongTo someone? :

In brief, if you define an OTM without declaring a 'belongsTo' property as we have, you're Child instances can exist on their own, without the need of being attached to a parent. One side effect of this is that the cascading save behaviour we saw before does not apply here... you will need to call save() on each domain separately.

So there we have it.... defining, creating and querying a One-To-Many relationship in Grails... I hope this has been helpful to anyone that comes across it! Happy trails!

Resources :
http://adhockery.blogspot.com/2009/06/querying-by-association-redux.html
http://www.grails.org/GORM+-+Defining+relationships

Constraining across relationships:
http://johnrellis.blogspot.com/2009/09/grails-constraints-across-relationships.html

Wednesday, September 30, 2009

Grails Constraints Across Relationships

Holy Moley am I glad to see the back of this problem!

This blog post is a summary of an issue I raised on the Grails User mailing list, here, and let me first give thanks to Dave Klein and Daniel Honig who really helped in figuring out this issue and explaining it to me, even though the time difference meant I could not stay awake any longer :)

OK so, suppose we have the following domains:


class Parent {

static hasMany = [children: Child]

String name;

static constraints = {
}
}



and ...



class Child {
static belongsTo = [parent: Parent]

String name

static constraints = {
name nullable: false, blank: false
}
}



Here we can see typical one to many relationship which allows me (you) to do the following in say, a Grails Service:



def parent = new Parent(name:"Father")
def billy= new Child(name:"Billy")
parent.addToChidren(billy)
parent.save()



So what happens here? We create a Parent, add a child to it, call save() on the parent and because the child "belongsTo" the parent, the child instance is saved too. That's just good parenting in my opinion!

OK so, our Parent doesn't want to be confused and have 2 children with the same name, nothing wrong with this, it's just this parents preference! So the knee-jerk reaction is to add a "unique:true" constraint to the Child domain.... hmmm wait a minute, if we do this, this means that we can only have one child named "Billy" in the entire world, hmmm that wont work. So thinking about it again, we want the Child's name to be unique only for a specific parent, so maybe we do something like "unique:'parent'" on the name property of the Child domain. If we do this, our Child domain becomes:


class Child {
static belongsTo = [parent: Parent]

String name

static constraints = {
name nullable: false, blank: false, unique: 'parent'
}
}


OK so, now lets start afresh:


def parent = new Parent(name:"Father")
def billy= new Child(name:"Billy")
def robert= new Child(name:"Robert")
parent.addToChidren(billy)
parent.addToChidren(robert)
parent.save()


Uh oh, something has gone wrong, I am now getting this madness when i try to save(): "InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: Parent".... Hmmm, something is not quite right here.

After some very good debugging from the aforementioned Groovynauts, it was discovered that Grails needed the Parent to be persisted before proper validation could take place on the children. This is only a minor annoyance if we wrap the code in a withTransaction{} block, why you ask? If we use a withTransaction block we can save our Parent confident in the fact that if any constraints fail on our children (or something else goes wrong) we will not have Parents in our database missing their children. Grails will roll back and remove the Parent from the database for us. So here it is (place this in a service method for instance):


Parent.withTransaction{
def parent = new Parent(name:"Father")
parent.save(flush:true)

def billy= new Child(name:"Billy")
def robert= new Child(name:"Robert")
parent.addToChidren(billy)
parent.addToChidren(robert)
parent.save(flush:true)
}


And there we have it, no parent can have multiple children that have the same name! For more info on how this was all figured out, check the link to the user list above, also check here for how to do something like this in a single domain and not across relationships.

Oh and if you do attempt to add children with the same, a "org.springframework.dao.DataIntegrityViolationException" is thrown!

Thanks again to the grails community! I hope someone finds this blog useful and doesn't lose as may hours (days?) as i did :)

Oh and one more thing, if you add unique:'parent' after you have done some dev work, i.e. your database is already built, you may need to rebuild the database as MySql did not update the indexes for me, might save some more confusion!