Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do I need to assign a Grails hasOne relationship in both directions?

Must I assign the relationship in each direction? Using the textbook domain classes for the hasOne relationship, this appears necessary from my testing so far to get the instances to recognize each other:

def face = new Face()
def nose = new Nose()
face.nose = nose
nose.face = face

I don't know why, though. And it's unconventionally awkward. I'm expecting there's a better way.

EDIT:

I need help with the relationship assignment. I do not need information on the mechanics of setting up a hasOne relationship in the domain classes, or a discussion about the wisdom of bi-directional references. I want to know why it takes more than a single statement to set the relationship between a nose instance and a face instance.

My two-statement solution is based on trouble I'm having in a complex application. I am going to try to reproduce my experience in a simple example.

like image 886
Jim Norman Avatar asked Jun 03 '11 01:06

Jim Norman


1 Answers

Direct Answer

The need to set up the relationships, i.e. to assign nose to face and face to node, isn't really awkward. Hibernate is a RELATIONSHIP mapper, so you need to make the relationships explicit. That said, you can write less code by defining a

setNose(nose){
   this.nose = nose
   nose.face = this
}

on Face. Now when you do face.nose = nose, the setter gets invoked for you, and the relationship is setup the way you want.

General Useful Thoughts

In general, you do not need to assign the relationship in both directions. It is perfectly valid to have unidirectional or bidirectional relationships.

However, hasOne definition has very specific implications. The documentation very clearly states the purpose of hasOne is to tell hibernate to put the key that defines the relationship in the child, in this case Nose. If you think carefully about it, you will realize that the relationship should be bidirectional. Here are the thought points:

1) you define the hasOne on Face (i.e. the parent).
2) even though you have defined hasOne on the parent, the underlying table that is affected is the child's table (i.e. the face_id column on Nose)
3) Since the foreign key is on the child, the child must have a reference to its parent. If it didn't, you would have a property on the child that is a foreign key but is not related to an object.
4) Remember, you are using the ORM's machinery to define the relationship. While you could manually add a face_id field to Nose, and set up the values in code (i.e. manage the relationship yourself and not let the ORM tool do it), the fact that you are using ORM explicitly means the ORM tool will manage the relationship for you.

EDIT -- now reading my answer, I was unconvinced, so I wrote a test. I defined the Face and Nose classes as shown in the hasOne documentation, but I did not define the Face on the Nose. I tried to make it unidirectional. I wrote a test to see what what happen. Here is the test

class FaceTests extends GroovyTestCase {

    public void testStuff(){
        Face face = new Face()
        Nose nose = new Nose()

        face.nose = nose;
        face.save(flush:true)

        assertNotNull face.id
        assertNotNull nose.id
    }

}

and the result is an exception that contains

hasOne property [Face.nose] is not bidirectional. Specify the other side of the relationship!

So the framework even makes sure, for you, that when you use hasOne, you have a bidirectional relationship.

like image 136
hvgotcodes Avatar answered Sep 22 '22 17:09

hvgotcodes