I'm making a script using some already created (not by me) domain classes from grails.
class Person extends OAP {
static hasMany = [addresses: Address]
(...)
}
class Address {
static belongsTo = [oap: OAP]
(...)
}
class OAP has no reference to Address.
So I was trying to do:
p.save()
a.oap = p
println a.oap
a.save()
with p being Person and a being Address, but although it prints the correct person on the console, the reference is not saved on the address table (oap_id stays null)
P.S.: It may not be the best relationship set-up in grails, but that's what I have to work with
Try p.addToAddresses(a) and then p.save(). It should save both p and a.
See http://grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html#5.2.4%20Sets,%20Lists%20and%20Maps
I have no idea how GORM will behave in this situation because you have essentially entered into this weird zone where you have a unidirectional hasMany on Person which results in a SAVE-UPDATE cascade behavior from Person and a NONE on Address. Then you also have a unidirectional one-to-one between Person and OAP which results in an ALL cascade behavior on the OAP side, and a NONE on the Address side. So I'm not sure what to even expect here. You need to fix the relationship to either:
Make it so OAP and not Person hasMany = [address:Address]
Make it so Address belongsTo = [person:Person]
Or, give some additional explanation as to what you're trying to do in your relationship and we can go from there.
Please try with this, it resolved my problem
p.addToAddresses(a);
p.save(flush:true)
Related
While migrating an existing app from Grails 2.5 to 3.1, I ran into an odd issue with a bi-directional one-to-one relationship.
Imagine a simple model with a User and Employee objects. A User represents a generic user account. Not all users are Employees but all Employees are Users. Moreover Employees have references to managers, supervisors, etc (also User instances). User is the owning side of the relationship.
class User {
Employee employee
static mappedBy = [employee: "user"]
static hasOne = [employee: Employee]
static constraints = {
employee(nullable:true)
}
}
class Employee {
User user // represents employee's own account, a bi-directional one-to-one
User supervisor // represents a supervisor
static belongsTo = [user: User]
static constraints = {
user(unique:true)
supervisor(nullable:true)
}
}
The trouble after upgrading to Grails 3 is that in the create mode, this results in supervisor_id column of employee table being generated as NOT NULL, whereas in Grails 2 it was nullable as expected (with only user_id being NOT NULL).
I tested this with Grails 3.1.12 and 3.2.0, getting the same behavior with both. Am I doing anything stupid in my domain class declarations? I've tried multiple mappings to achieve the same behavior as in Grails 2.5 without luck. In some cases I'm even getting a foreign key on both sides of the relationship...
I don't know why your code was working with previous version of Grails, but it is wrong.
When you use hasMany and belongsTo, it is not necessary to define other property in the child object, and you don't need also to use the mappedBy property on the parent, and the same with the parent (property employee at User)
Grails doesn't need anything else to know which is the bidirectional property on both classes, and the constraint user(unique : true) neither.
So your classes should look like this:
class User {
static hasOne = [employee: Employee]
static constraints = {
employee(nullable: true)
}
}
class Employee {
User supervisor // represents a supervisor
static belongsTo = [user: User]
static constraints = {
supervisor(nullable:true)
}
}
It could be nice to know how is your DB structure. But in this way all foreign keys are stored in the employee table. But of course you could navigate from both entities. If you have different structure you could map your current database with this model. See this
employee.user
user.employee
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
UPDATED
I have a domain classes as below
class Training{
// has one createdBy object references User domain and
// has one course object references Course domain
// has One Trainer1 and Trainer2 objects refernces Trainer Object.
}
class Trainer{
String name
//can have many trainings.
//If Trainer gets deleted, the trainings of him must be deleted
}
Class User{
String name
// can have many trainings.
}
class Course{
String name
//If Course gets deleted, Trainings of this course must be deleted
// can have many trainings.
}
I have got a training create page, Where I have to populate already saved Course, User, Trainer1 and Trainer2. I am not saving them while Creating the training.
So, How to specify the relationship in grails
You did not put any effort to searching answer for yourslef. There are plenty basic examples and blog posts how to map relations in Grails. You should start with Grails documentation of GORM - Grails' object relational mapping. You can find it here.
I see some minor flaws in yout initial design: ie why should training be deleted if user is deleted when trainings will obviously tie with many users. Can training exists without trainers or vice versa ?
I would start with something like this:
Class Training {
static hasMany = [users: User, trainers: Trainer]
static belongsTo = Course
}
Class Trainer {
String name
}
Class User {
String name
}
Class Course {
String name
static hasMany = [trainings: Training]
}
EDIT: I have to agree with Tomasz, you have jumped here too early without searching for answers yourself. Grails.org has good documentation about GORM with examples too.
I am a bit puzzled on the following..
A domain model consists of a Person who has Addresses, these Address
can also be referenced by other Persons..
So Person A has addresses 1, 2, 3 and Person B has adresses 1, 4 ,5
No when i delete Person A, Person B also loses his address 1!! I was
under the impression that the cascade: 'all-delete-orphan' was meant
for these kind off situations. But probably i am making a mistake!
How can i tackle this?
class Person {
Address address
Address addressPostal
Address addressInvoice
static mapping = {
address cascade:'all-delete-orphan'
addressPostal cascade:'all-delete-orphan'
addressInvoice cascade:'all-delete-orphan'
}
}
class Address {
String street
String streetNo
String streetSuffix
}
I think all-delete-orphan works the other way. When you remove the many object from the collection on the one side, the object gets auto-deleted. Otherwise, it would just get "orphaned" with null as the foreign key.
You could use it the other way. For example, Address had a static hasMany = [persons: Person] and you removed the person from it, all-delete-orphan would remove the Person.
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"
}