While reading the grails official guide, I've got question about belongsTo with many-to-one relationship.
When I define two classes, Face and Nose as listed below:
class Face {
String name
Nose nose
static constraints = {
}
}
class Nose {
String color
static belongsTo = [face: Face]
static constraints = {
}
}
I think that we can make Face instance in two ways:
Making face with nose at the same time
def rudolph = new Face(name: 'Rudolph', nose: new Nose('color': 'Red')).save(failOnError: true)
Making a nose, and face in sequence
def nose = new Nose(color: 'Red').save(failOnError: true)
def rudolph = new Face(name: 'Rudolph', nose: nose).save(failOnError: true)
However, both give me an error like:
Fatal error running tests: Validation Error(s) occurred during save():
- Field error in object 'relationship.Nose' on field 'face': rejected value [null]; codes
Of course, if I put the constraints in Nose, it works:
class Nose {
String color
static belongsTo = [face: Face]
static constraints = {
face nullable: true
}
}
I'm not sure if the back reference property must be always nullable or not.
Another question is the following static property works because it doesn't have "face" property:
static belongsTo = Face
If it doesn't have back reference property name, why do we define belongsTo property?
In the case you define one-to-one relationship and therefore you don't need to have "belongsTo". You can remove it from "Nose" class and it will be fine. More over you don't have to save "Nose" instance, just create it and add it to "Face" and save "Face" instance. Details: http://grails.org/doc/latest/ref/Domain%20Classes/hasOne.html
"belongsTo" needs to be defined when you have one-to-many or many-to-many relationship. It has to do with saves and cascading deletes. Link for the question "why": http://grails.org/doc/latest/ref/Domain%20Classes/belongsTo.html
Related
First please go here
https://grails.github.io/grails-doc/3.0.x/guide/GORM.html#manyToOneAndOneToOne
If you scroll few lines, you'll see the line
Finally, it's a good idea to add a unique constraint on one side of the one-to-one relationship:
The code is
class Face {
static hasOne = [nose:Nose]
static constraints = {
nose unique: true
}
}
I took out the changelog of the following two domain definitions without the unique:true
class Nose {
String name
Cat cat
static constraints = {
}
}
class Cat {
String name
static hasOne = [nose: Nose]
static constraints = {
}
}
and the changelog shows that the unique has been added
<changeSet author="S (generated)" id="1452758231706-5">
<createIndex indexName="cat_id_unique_1452758231629" tableName="nose" unique="true">
<column name="cat_id"/>
</createIndex>
</changeSet>
So, although this is not a big deal to have few redundant statements in the docs i wanted to make sure that the unique:true is really not necessary. Specifying hasOne and having a back reference is enough to make the relationship truly one to one. Is that correct? Thanks!
The two domains are as follows:
class Face {
static hasOne = [nose:Nose]
static constraints = {
nose unique: true
}
}
class Nose {
Face face
static constraints = {
}
}
The foreign key will be in the Nose table. The Nose table has reference to Face table. The face_id column in Nose table is not nullable. I was wondering if it is possible to make this column nullable. I tried the following constraint variations but none of them seem to alter the structure of the tables.
class Face {
static hasOne = [nose:Nose]
static constraints = {
nose unique: true
nose nullable: true
}
}
I also tried the following
class Nose {
Face face
static constraints = {
face nullable:true
}
}
I need a one to one relationship between these two domains but also want to be able to create independent Nose or Face entities or records. Is that possible? If so, can you suggest a way? Thanks! Appreciate it!
You can create a third relationship domain which would specify the relation between Face and Nose.
FaceNose{
Face face
Nose nose
static constraint{
nose nullable:false,unique:true
face nullable:false,unique:true
}
}
class Face {
//face class properties
static constraints = {
}
}
class Nose {
//nose class properties
static constraints = {
}
}
This fulfils your Need for one to one relationship with independent entries for both.
With hasOne relationship, Grails creates "not null" foreign key.
class Face{
..
static hasOne = [nose : Nose]
}
class Nose{
Face face
}
creates "face_id" in the nose table with "not null" face_id.
Work around
class Face{
..
static hasMany = [noses: Nose]
static transients = ['getNose']
Nose getNose{
return this.noses[0] ?: null
}
}
face.nose // this would work
What I want: when I call validate (or save) on a parent domain during a unit test, it will check the constraints of all children domains AND their children AND their children etc.
What is happening: during a unit test for a service, validate keeps returning true despite constraints not being met for children domains. Please help...
I had two change two things about my code:
1) I had to make sure all domains were mocked for the unit test.
2) I had to make sure each child domain's back reference to the parent was populated. obj.addToSomeProperty(property) automatically populated back references for one-to-many objects, but for one-to-one I had to set it manually.
Service Unit Test:
#TestFor(GardenerService)
#Mock([Orchard, Plot, Tree, Fruit])
class GardenerServiceSpec extends Specification {
void "test save and fetch"() {
setup:
Fruit fruit = new Fruit(name:"Peach")
Tree tree = new Tree(height:6)
tree.addToFruits(fruit)
Plot plot = new Plot(litersOfWaterAdded:10)
plot.tree = tree // Must be a better way... why didn't setTree(tree)
tree.plot = plot // set the back reference
Orchard orchard = new Orchard(orchardName:"Eden")
orchard.addToPlots(plot)
when:
orchard.save(flush: true)
then:
orchard.validate()
Fruit.findByName("Peach").tree.plot.orchard.name == "Eden"
Tree.findByHeight(6).plot.litersOfWaterAdded == 10
}
}
Domains:
class Orchard {
String orchardName
static hasMany = [plots: Plot]
}
class Plot{
static belongsTo = [orchard: Orchard]
int litersOfWaterAdded
static hasOne = [tree: Tree]
}
class Tree {
static belongsTo = [plot: Plot]
int height
static hasMany = [fruits: Fruit]
}
class Fruit {
static belongsTo = [tree: Tree]
String name
String description
static constraints = { description nullable: true }
}
I am new to Grails.
Can I use "hasOne" or "hasMany" without using "belongsTo" to another domain-class?
Thanks in advance.
Yes, you can. See examples in Grails doc: http://grails.org/doc/2.3.8/guide/GORM.html#manyToOneAndOneToOne
hasMany (without belongsTo) example from the doc:
A one-to-many relationship is when one class, example Author, has many
instances of another class, example Book. With Grails you define such
a relationship with the hasMany setting:
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
}
In this case we have a unidirectional one-to-many. Grails will, by
default, map this kind of relationship with a join table.
hasOne (without belongsTo) example from the doc:
Example C
class Face {
static hasOne = [nose:Nose]
}
class Nose {
Face face
}
Note that using this property puts the foreign key on the inverse
table to the previous example, so in this case the foreign key column
is stored in the nose table inside a column called face_id. Also,
hasOne only works with bidirectional relationships.
Finally, it's a good idea to add a unique constraint on one side of
the one-to-one relationship:
class Face {
static hasOne = [nose:Nose]
static constraints = {
nose unique: true
}
}
class Nose {
Face face
}
Yes you can, but it behave differently
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
}
In this case if you delete Author the books still existing and are independent.
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
static belongsTo = [author: Author]
}
In this other case if you delete the Author it will delete all the books pf that author in cascade.
Many-to-one/one-to-one: saves and deletes cascade from the owner to the dependant (the class with the belongsTo).
One-to-many: saves always cascade from the one side to the many side, but if the many side has belongsTo, then deletes also cascade in that direction.
Many-to-many: only saves cascade from the "owner" to the "dependant", not deletes.
http://grails.org/doc/2.3.x/ref/Domain%20Classes/belongsTo.html
yes very easy like a class defintion but only specify hasMany but no need for hasOne
class Student {
String name
User userProfile
static hasMany =[files:File]
}
class User {
String uname
Student student
}
class File {
String path
Student student // specify the belongs to like this no belong to
}
Done!!
I have a Grails domain class that is a hierarchy of categories. Each Category has a parent category (except for the root category which is null).
class Category {
String name
static mapping = {
cache true
name index:'category_name_idx'
}
static belongsTo = [parent:Category]
static constraints = {
parent(nullable:true)
}
}
My problem: deletes cascade exactly opposite of what I'd expect:
someSubCategory.delete() deletes the category then tries to delete the parent category (which fails with an integrity violation if the parent has other children).
parentCategory.delete() does NOT cascade delete its children, but instead just fails with an integrity violation.
What am I doing wrong? My understanding is that the 'belongsTo' above should tell the GORM to cascade deletes from the parent to all children, but not from a child to its parent.
If I am understanding correctly a Category belongs to a parent and a parent can have multiple children, so I think you need a hasMany relationship, something like this:
class Category {
String name
static mapping = {
cache true
name index:'category_name_idx'
}
static belongsTo = [parent:Category]
static hasMany = [children: Category]
static constraints = {
parent(nullable:true)
}
}
I had had similar structures and never have issues with the delete doing it this way.
Hope this helps!
It's not an answer, but I found a workaround to my own question. You can remove the belongsTo = [parent:Category], replacing it with a simple instance variable. This stops subCategory.delete() from cascading to the parent.
class Category {
String name
Category parent
static mapping = {
cache true
name index:'category_name_idx'
}
static constraints = {
parent(nullable:true)
}
}