grails hasOne vs direct member variable
Asked Answered
S

4

24

Let's say I have a grails domain class that looks like

class Person {
    Address address
}

I could also declare it as

class Person {
  static hasOne = [address:Address]
}

The second way would move the foreign key to the Address table rather than the person table.

What are the practical benefits (or disadvantages) of doing this one way vs the other? As far as I understand, they will both use foreign keys, it's just a matter of where the foreign key lives.

Spooner answered 15/10, 2012 at 16:46 Comment(0)
U
30

If the foreign key exists on the address table, then that address can only have one person. If the foreign key is on the person table, multiple persons can have the same address.

It's not about what way is better/worse. It's about what is the correct way to model your data.

Underpinning answered 15/10, 2012 at 17:9 Comment(1)
That makes sense, thanks. Sorry a little bit of a brain fart from me here. I got wrapped up in the grails mechanisms and didn't step back to think about it from a database perspective.Spooner
F
25

I find the use of hasOne in Grails to be particularly confusing. For example, this question asks what happens when a toOne relationship is declared as follows:

class Person {
  static hasOne = [address: Address]
}

As stated above, this causes the person_id foreign key to appear in the Address table, which means that each Address can only point at one Person. What I find bizarre, then, is that even though the code is written as "a Person has one Address", the actual result is that "an Address has one Person".

And in fact, defined solely as above, there's nothing (at the database level) preventing more than one Address record from pointing at the same Person, which means that a Person doesn't really have to have one Address after all.

Interestingly, you'd get the same database representation if you created the Address class like this:

class Address {
    Person person
}

The person_id foreign key will be in the Address table, just as like in the previous example, but obviously, you can't get from the Person to the Address in the code without defining that relationship in some way as well.

Also interesting is that, if you were modeling a toMany relationship from Person to Address in the database, you'd use the same table layout. You'd put the parent's primary key (person_id) into the child table. From a database perspective then, using hasOne creates the same structure that a toMany relationship would create.

Of course, we're not just creating database tables, we're creating Grails domain classes that have some behavior associated with them, and some enforcement of relationship semantics. In this particular business example, you probably don't want to share the same Address record with multiple People, you just want to store the Address separately (maybe preparing for the day when a Person has multiple Addresses). I'd probably vote for this approach:

class Person {
    Address address

    static constraints = {
        address unique:true
    }
}

The address_id foreign key will be in the Person table, and the unique constraint will enforce that no two Person records are pointing at the same Address.

Fabria answered 22/4, 2013 at 19:34 Comment(2)
And hasOne appears to have performance implications (at least in 2.2.0, which I am stuck with). I was profiling and found that the association was being lazily loaded, even though I wasn't using it, resulting in lots of unnecessary trips to the DB. Switching it to a member variable seems to have eliminated that.Petr
+1 to what @CharlesWood says. For me (3.3.11) the hasOne property seems to always get fetched even when not used, and always gets fetched N+1 style (one query per parent) even when batchSize is specified.Paracasein
I
9

I suggest the following...

class Person {
  ...
  static hasOne = [address: Address]
}

class Address {
    ...
    static belongsTo = [person: Person]
}

A person has one address and the address belongs to one person.

By this way, when you delete a person, the address will be deleted too, without problems.

I think this is the better way to do this.

Intrigue answered 31/1, 2014 at 20:39 Comment(1)
Although houses aren't typically deleted when you die :DPetr
C
7

The Person "hasOne" Address and the Address "belongsTo" the Person.

Having the foreign key on the "child" table makes more sense because this way the child would break if the parent is missing. A Person can exist without its address but a "person's address" without a person makes no sense. So the Address should be the weaker side of the relationship.

Doing it this way in grails both entities will have the reference to each other so it won't feel weird. Also the default cascading behaviour will save and delete the Address when you save the Person but if you want to delete the Address the Person will remain saved.

Coulomb answered 14/4, 2014 at 15:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.