Given the following domain classes:
class Location {
String city
static belongsTo = [ author: Author ]
}
class Author {
String name
Location location
}
when I do this:
def l = new Location(city: "Boston")
l.save()
def a = new Author(name: "Niall Ferguson", location: l)
a.save()
I can delete my author with a.delete() but if I try to delete my location with l.delete() I get this error:
org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [Location#17]
ok fine so let me remove the object to be deleted object from associations and try again:
author.location = null // Assuming these two statements are the right way
location.author = null // to to this.
now when I try l.delete() again I get this:
ERROR util.JDBCExceptionReporter - Referential integrity constraint violation: "FKAC2D218B9615BDBA: PUBLIC.AUTHOR FOREIGN KEY(LOCATION_ID) REFERENCES PUBLIC.LOCATION(ID) (18)";
So what am I doing wrong? In this scenario is it even possible to delete the location and keep the author?
When I tried to reproduce your code, my first error was in l.save(). In your class, the field author in your domain Location is required. You can not save a location without a author in your example. You should always check the return value of save() or use failOnError: true to uncover little mistakes like that.
That being said, it seems to me you are modelling a one-to-one relationship. Not a many-to-one, otherwise how can a Location have one Author? (I agree that the grails documentation is a little confusing in this matter)
Instead, I create the following classes, assuming that a author have a location, but the same location can be referenced by many authors.
class Author {
String name
Location location
}
class Location {
String city
}
Now you are left with your Referential integrity constraint violation, basically, you can not delete a location without deleting first all the authors that are related to that location.
If you want the delete cascading to work, I would use a bidirectional one-to-many relationship like this
class Author {
String name
static belongsTo = [location: Location]
}
class Location {
String city
static hasMany = [authors: Author]
}
And about your last question, as I understand your scenario, you can not delete a location and keep the authors. I am assuming that many authors are related to the same location. So, if you delete the location, what should happen with the references from all the authors to that location?
Related
Is it possible to map the following with GORM?
I want to get rid off all associated events when I delete a person.
Person object should not have a link to events.( I want to avoid using hasMany on Person domain)
class Person {
String username
}
class Event {
String description
static belongsTo = [person:Person]
}
I'm getting now a 'Referential integrity constraint violation' when doing person.delete() because events are not removed before deleting person.
I don't think that is possible without using hasMany (speaking of which, why do you want to avoid that anyway?)
This SO Question states:
Hibernate only cascades along the defined associations. If A knows
nothing about Bs, nothing you do with A will affect Bs.
Use static hasMany and bam, problem fixed.
Edit:
The only way I think you could achieve this is using beforeDelete on the Person class to delete all the associated Events, i.e.
class Person {
def beforeDelete() {
def events = Event.findAllByPerson(this)
for (e in events) {
e.delete()
}
}
}
See the documentation on Events and Auto Timestamping for more info on that.
The above will not work
Why not define a no reference mapping:
class Person {
String username
static hasMany=[Events]
}
This way there is no actual bindings of events to person but a person can have many events
I have some domain class Incident,Problem, Category, Impact, Urgency etc.
Class Incident
{
Category category
String subject
Impact impact
}
Class Problem
{
Urgency urgency
Category category
String title
}
Class Category
{
String categoryName
String description
}
now, some rows are inserted into this class. now if I am deleting category it throws error like 'grails cannot delete or update a parent row'..
so what I have to do for deleting?
The problem is - you have reference to Category in Incident and Problem classes, so database tables for those classes will have Foreign key on category table, so you can not delete a category untill you either remove those incidents/problems or update those incidents problems and set category to null (you will have to make them as nullable in domain constraints)
So either you do
Problem.executeUpdate('update Problem p set category = null where category = ?', [category])
Same for incidents
Or you can model your domain classes using belongsTo and hasMany and grails will handle every thing automatically
Some thing like
class Problem {
static belongsTo = [category:Category]
}
class Category {
static hasMany = [
problems: Problem
]
static mappings = {
problems cascade: "all-delete-orphan"
}
}
I would prefer to manage relationships using belongsTo, hasMany, hasOne rather then just using references, it expresses the model better.
It depends on your domain model as well, in your business can problems, incidents exist without a category ! or they must belong to some category. If your answer is first option, your dont want to cascade delete, but update those incidents/problems with null category, if your answer is second option - you needs cascade all-delete-orphan
How your Category looks like, is it belongsTo Incident domain class,if category belongs to some domain class you can not delete it.
Ref : See here
I've been struggling to produce the right configurations to produce cascade-delete behaviour in a relatively simple Grails project.
Say I have the following simple domain classes:
class Author {
String name
static constraints = {
}
}
and
class Book {
String title
Author author
static constraints = {
}
}
If I create an author, and then create a book written by that author, I am not able to delete the Author without first manually deleting the book. I get an "Integrity constraint violation". This isn't suprising as MySQL (my underlying database) is created by Grails with a "foreign key constraint" on the "author" column of the "book" table as "Restrict" (and this behaviour is consistent with expectations from the Grails documentation as I understand it).
Now, if I were to manually change the underlying database constraint on the "author" column of the book table from "Restrict" to "Cascade", I get the behaviour I want. Namely, that if you delete the Author, all their books are also deleted.
So, what I'd like to do is change my Grails "Book" class in a way that creates the "book" table with "on delete cascade" on the author column. I've been reading plenty of information about doing this sort of thing and GORM defaults using "belongsTo" and explicit "mappings".
One way to do this, based on the documentation for "belongsTo", seemed to be to change the line in the Book class from:
Author author
to
static belongsTo = [author: Author]
Thereby making it explicit that the Author is the "owning side" of the relationship. The documentation seems to suggest that this should generate the cascade-delete behaviour I'm after. However, it doesn't work unless I add an explicit "hasMany = [books:Book]" into the Author class. I don't wish to do this. (This wish makes more sense in my actual business domain, but even just as an exercise in understanding, I don't yet get why I have to have the Author domain class know about books explicitly).
I'd just like a grails setting to change the Book class to produce the "cascade delete" setting in the database, without having to change the Author class. I tried using an explicit mapping like:
static mapping = {
author cascade: 'all'
}
and combinations of this with other explicit mapping or "belongsTo" options. This didn't work.
Noting that a simple change to the "constraints" in the underlying SQL database provides the behaviour I want, is there a way to get this through Grails? If not, have I misunderstood something fundamental here or am I trying to do something stupid?
I think you are missing the required field of type Book in Author.
Here's the sample code, which is as per the documentation (tested and works)
class Author {
String name
Book book //you are probably missing this field
static constraints = {
}
}
class Book {
String name
static belongsTo = [author: Author]
static constraints = {
}
}
Test case:
#TestFor(Author)
#Mock([Book])
class AuthorTests {
#Test
void testAuthorBookCascades() {
Author a = new Author(name: "Douglas Adams")
Book b = new Book(name: "So Long, and Thanks for all the Fish")
a.book = b
a.save()
assert Author.count() == 1
assert Book.count() == 1
a.delete()
assert Author.count() == 0
assert Book.count() == 0
}
}
As you can see, you need the Book argument in Author. No need for the hasMany or hasOne clause.
Consider the following domain classes
class Business{
static hasMany = [contacts:ContactPerson]
}
class ContactPerson{
}
Given the following domain classes, say we use the following examples:
Alice is ACME's contact person. Bob and Carol are Calamity Corp's contact person.
Say I wanted to remove Bob in the ContactPerson table. Thus:
bob.delete(flush:true)
But the code will result to the server complaining about contraints:
ERROR: update or delete on table "contact_person" violates foreign key constraint
"fk4a69c6b329ef2fe1" on table "business_contact_person"
Detail: Key (id)=(174) is still referenced from table "business_contact_person".
In this context, the exception is thrown because Bob is still associated with Calamity Corp.
How do I delete Bob from the ContactPerson table? I wanted to remove Bob from the database altogether. I don't want to add belongsTo since I don't want to cascade the delete to Business' children (that is to say, if I delete Acme Corp from the database, I still want Alice to be in the system).
I've seen examples on disassociating the parent from the child but not the other way around.
I believe by simply adding a belongsTo, the cascade should work as expected...
class Business{
static hasMany = [ contacts:ContactPerson ]
}
class ContactPerson{
static belongsTo = [ business: Business ]
}
Ok. So with a little more digging, I finally got the codes that I wanted. John Rellis' post on Relationship Advice : Grails One-To-Many was especially helpful. In this article, he mentions how to query from children to parent (which is exactly what I am looking for).
Going back to the question, I wanted to remove Bob from the ContactPerson table but given the relationship between Business and ContactPerson simply calling bob.delete() simply would not do. What I need is to look for all businesses associated with Bob and remove the association. Thus the code below:
def bob = ContactPerson.get(params.id)
def criteria = Business.createCriteria()
def businesses = criteria.listDistinct{
createAlias("contactPersons","c")
eq("c.id", bob.id)
}
businesses.each{business->
business.removeFromContactPersons(bob)
business.save(flush:true)
}
bob.delete(flush:true)
I also added a new mapping to the Business domain model:
static mapping = {
children cascade:"all-delete-orphan"
}
In an ordinary one-to-many mapping the "one"-side is the owner of the association. Why would anyone use the belongsTo-mapping for such a mapping? Am I missing some side-effect of specifying belongsTo?
In other words: what are the effects of specifying a belongsTo-mapping in GORM vs. not specifying it?
Whether to specify belongsTo depends upon the type of referential action you want.
If you want Grails to do On Delete, CASCADE referential action, then DO specify belongsTo. If you want Grails to do On Delete, RESTRICT referential action, then DON'T specify belongsTo.
e.g.
// "belongsTo" makes sense for me here.
class Country {
String name
static hasMany = [states:State]
}
class State {
String name;
// I want all states to be deleted when a country is deleted.
static belongsTo = Country
}
// Another example, belongsTo doesn't make sense here
class Team {
String name
static hasMany = [players:Player]
}
class Player {
String name
// I want that a team should not be allowed to be deleted if it has any players, so no "belongsTo" here.
}
Hope this helps.
Specifying belongsTo allows Grails to transparently cascade updates, saves and deletes to the object's children. Without belongsTo, if you attempt to delete a master record, you'll end up getting a foreign key violation if it has any details it owns.