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!
Related
I have a trait and the following domain classes:
trait Named {
String name
static constraints = {
name blank:false, unique:true
}
}
#Entity
class Person implements Named {
String fullName
static constraints = {
fullName nullable:true
}
}
#Entity
class Category implements Named {
}
In this setup the Named.constraints are working fine for Category, but are ignored for Person.
How can I include the constrains from a trait in a constraints block of an implementing domain class?
How can I include the constrains from a trait in a constraints block
of an implementing domain class?
The framework doesn't support it.
I found the least painful way to re-use the constraints.
I added a new class:
class CommonConstraints {
static name = {
name blank:false, unique:true
}
}
Then instead of importFrom Named which didn't work, I sprinkle some groovy magic:
#Entity
class Person implements Named {
String imgUrl
String fullName
static constraints = {
CommonConstraints.name.delegate = delegate
CommonConstraints.name()
fullName blank:false, unique:true
imgUrl url:true
}
}
And it works like charm.
Yes, the solution not consistent with inheritance/implementation model, but gets the job of code reuse done.
For the less tricky cases, where the domain class does not have it's own constraints, the ones from a trait will be used, like in my original question.
I'm pretty much certain I'm doing something wrong since this obviously works. Simplified classes:
class Person {
String name
static hasMany = [cats:Cat]
}
class Cat {
String name
Person person
static belongsTo = Person
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
Simple stuff, a person has many cats, cats must belong to only a single person.
Now when I do the following in a Service class, I get strange results:
delete(Cat cat) {
Person owner = cat.person
log.debug("Cats before removing ${cat} (id=${cat.id}): ${owner.cats} -- ${owner.cats*.id}")
owner.removeFromCats(cat);
log.debug("Removed from owner ${owner}, owner now has ${owner.cats} -- ${owner.cats*.id}")
log.debug("Cat to delete is now: ${cat} and belongs to... ${cat.person}")
cat.delete(flush:true)
}
And the error is "object would be resaved, blah blah"
org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)
The weird bit is the debug results, when called to remove cat "Fluffy" who's owned by "Bob":
Cats before removing Bob-Fluffy (id=1356): [Bob-Fluffy] -- [1356]
Removed from owner Bob, owner now has [null-Fluffy] -- [1356]
Cat to delete is now: null-Fluffy and belongs to... null
What's going on that "removeFrom" isn't actually removing the object from the collection? I cleaned and recompiled. Pretty much at a loss as to why I can't delete this object.
I would try to remove the field person as Person person and leave only the belongsTo field like this
class Cat {
String name
static belongsTo = [person:Person]
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
It looks like what was happening in my case is that cat.person has getting stale somehow, even though it's the first thing in the method. Calling cat.refresh() didn't work, but calling owner.refresh() after extracting it from the cat.
I would change the domain class as such.
class Person {
String name
static hasMany = [cats:Cat]
}
class Cat {
String name
Person person
// no need to add belongs to property here. it creates a join table that you may not need
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
In the service class
delete(Cat cat) {
cat.delete(flush:true)
}
Once you make the domain changes, start with a fresh database since the schema will change.
I think that should solve your problem.
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
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
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)
}
}