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)
}
}
Related
I have this classes:
class Parent{
static hasMany = [children:Child]
}
class Children{
static belongsTo = [Parent]
}
I want to do something like
Parent.findByChildren(ChildInstance)
In the database there is a table with the relationship id's, but I don't know how to access to it.
But that doesn't work, which is the proper way?
Thanks
Change your Children class belongsTo clause :
class Children{
static belongsTo = [parent: Parent]
}
This will allow you to access a child's parent instance : childInstance.parent
First I would change the relationship in Children domain to
static belongsTo = [parent: Parent] // suggested by #bassmartin
or
Parent parent
both does the same thing.
Once you have the ChildInstance and the reference to the parent, you can simply do
ChildInstance.parent // returns instance of parent
Similarly, if you want to find all the children of a parent, you can do
parent.children // return an array of children which you can iterate over.
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.
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'm creating a (theoretically) simple hasMany relationship within a domain class. I have two tables with a foreign key relationship between the two. Table 1's domain object is as follows:
Functionality{
String id
static hasMany = [functionalityControllers:FunctionalityController]
static mapping =
{
table 'schema.functionality'
id column:'FUNCTIONALITY_NAME', type:'string', generator:'assigned'
version false
}
}
and domain object 2
FunctionalityController
{
String id
String functionalityName
String controllerName
static mapping =
{
table 'schema.functionality_controller'
id column:'id', type:'string', generator:'assigned'
version:false
}
}
The issue I am having is that when I have the hasMany line inside of the Functionality domain object, the app won't start (both the app and the integration tests). The error is org.springframework.beans.factory.BeanCreationException leading to Invocation of init method failed; nested exception is java.lang.NullPointerException.
Any help would be appreciated.
UPDATE:
*Working Domains*:
class Functionality {
String id
static hasMany = [functionalityConts:FunctionalityCont]
static mapping =
{
table 'schema.functionality'
id column:'FUNCTIONALITY_NAME', type: 'string', generator: 'assigned'
functionalityConts( column:'functionality_name')
version false;
}
}
and
class FunctionalityCont {
String id
String functionalityName
String controllerName
static belongsTo = [functionality: Functionality]
static contraints = {
}
static mapping =
{
table 'schema.functionality_controller'
id column:'id', type: 'string', generator: 'assigned'
functionality(column:'FUNCTIONALITY_NAME')
version false;
}
}
Well 2 things...
1.I'm not sure but I guess that your domain class with the prefix 'Controller' maybe is the responsible, this is because grails is convention over configuration and by convention the controller class ends with Controller prefix and are in the controller folder, in this case is a lil' confusing
2.In GORM and in this case the relationship between objects can be unidirectional or bidirectional, is your decision to choose one, but in both cases there are different implementations, the class Functionality(btw is missing the 'class' word) has the right relationship to FunctionalityController through hasMany, but FunctionalityController doesn't knows about the relationship, so you can implement:
// For unidirectional
static belongsTo = Functionality
// For bidirectional
static belongsTo = [functionality:Functionality]
// Or put an instance of Functionality in your domain class,
// not common, and you manage the relationship
Functionality functionality
So check it out and tell us pls...
Regards
Try adding
static belongsTo = [functionality: Functionality]
to your FunctionalityController class. I suspect there is more to your error than what you've shown, but generally a hasMany needs an owning side to it. Since that is where the foreign key actually lives.
What I am trying to do is have two domains that reference each other. However one does not necessarily have the other or belongs to the other. Each object from both domains can reference 0 or 1 object from the other domain.
I have this code, but it doesnt work:
class Domain1{
//declare some vars
...
static belongsTo = [domain2Object:Domain2]
static constraints = {
domain2Object(nullable:true)
}
}
Using hasOne with the nullable:true constrain works, but it doesnt work if the other side has the same thing. The point is that I want to be able to delete any object from any of the domains that is referring an object from the another domain without causing the referred object to be deleted as well. So like I said, no object belongs to the other, they just reference each other.
=========================================================================================
Response:
using this in both domain classes:
class ClassB {
static hasOne = [classA:ClassA]
def beforeDelete = {
classA?.delete()
}
}
static constraints = {
classA(nullable:true)
}
I get this error when i try to add an object of any of the two classes leaving the other class blank:
"Integrity constraint violation - no parent FK24742AC1AA048190 table: PENDINGORDER"
You can use "hasOne" onDelete event.
class ClassB {
static hasOne = [classA:ClassA]
def beforeDelete = {
classA?.delete()
}
}
Events and Auto Timestamping
I guess the exception happens because you are trying to delete an object by it's relation in the beforeDelete event. Remove your relation before deleting it like this :
class ClassA {
ClassB classB
static constraints = {
classB nullable: true
}
}
class ClassB {
ClassA classA
static constraints = {
classA nullable: true
}
def beforeDelete = {
classA?.classB? = null
classA?.delete(flush:true)
}
}