Understanding hasMany behavior in grails and how to save a relationship - grails

I'm making a model where a User fills out many questionnaires and the responses get saved to Questionresponse. I'm on grails 2.5.2
Test1
So I have two models
class User {
String username
...
static hasMany = [reponse: QuestionResponse]
}
class QuestionResponse {
String question_1
String question_2
...
}
With the above, a new DB table is created: user_questionresponse with two columns user_questionresponses_id and questionresponse_id. This seems like what I want. A user would have many questionresponses and those relationships would be saved in this table. However, I can't find out how to save data to this table.
For example, if I do:
def user = springSecurityService.currentUser
def questionnaire = new QuestionResponse(question_1: "foo", question_2: "bar")
//How do I link the user to this newly created questionnaire?
user.addToResponse(q).save(flush: true) //DOES NOT WORK.
Test2 (just add belongsTo)
class User {
String username
...
static hasMany = [reponse: QuestionResponse]
}
class QuestionResponse {
String question_1
String question_2
static belongsTo = [user: User]
...
}
If I add belongsTo to QuestionResponse a new column, user_id, gets created in the DB. Now if I run the same code as above, this user_id column has the id populated with that of the current user. However, the relationship table, user_questionresponse is still empty.
I am aware of the approach mentioned by Burt but I assume that should be required only for ManyToMany relationship. If that is required for all relationship, why isn't that the default?

In your first case, you have a OneToMany relationship between User and QuestionResponse with no side being the owner of the relationship. In this case to maintain the relationship between User and QuestionResponse, a third table is required. To persist data you need to do the:
userInstance.addToResponse(new QuestionResponse(question_1: "foo", question_2: "bar")).save(flush: true, failOnError: true)
You are doing user.addToReponse(q) instead it should be user.addToReponse(questionnaire), if it's not a typo and the data is actually not being stored, then check by adding the failOnError parameter to save() method. Sometimes grails save() method fails silently, it should tell you if this is the case.
In second case, you have added the parent to the relationship, so that means you don't need the third table to maintain the relationship. Grails will not create and populate the third table in this case.

The second approach (adding belongsTo in QuestionResponse) seems the right thing to do in your case, since QuestionResponse objects cannot exists without a user and cannot belong to different users.
In that case there's no need to use a third table.
When you run the app for the first time, grails created the relation table (because there was no belongsTo). When you run the app again with belongsTo grails adds the user_id field but DOES NOT DROP the relation table. That's why the table is there and is empty: it's not needed, but grails database auto-update feature only adds things, it does not remove anything.
The same applies to fields: if you remove a field from an entity you have to manually remove it from the database.

Related

how to make first column nullable in one to many relationship join table?

There seems to be a change in behavior from grails 2 to grails3. When i create a one to many relationship like
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
}
It will create a join table with columns author_books_id and book_id. In grails 3 it also adds a not null constraint on the first column. In grails 2 not null constraint is not applied. So when i upgrade to grails 3 it is breaking because there are already few records that have first column values to null. It works fine in grails 2 but with grails 3 the first column should not be null. Furthermore the join table is read only so i cannot remove the rows will null first column values. Is there a way to make the first column nullable = true by making changes in domains and not directly in migration file.
The code was extracted from the grails documentation. Please scroll down to one to many section.
6.2.1.2 One-to-many
http://docs.grails.org/3.0.17/guide/GORM.html
Try changing your Book class to this
class Book {
String title
Author author // this creates the belongs to relationship
}
static constraints = {
author nullable:true
}
Also when you recompile the code make sure to delete previous table and start fresh. Grails will not alter any table to delete anythings. It only adds new stuffs.

Grails/Gorm: how to filter a list of domain objects without affecting the database

Say we have something like the standard Book domain object and bookCategory object. In my controller I want to return a subset of list of books to the view. That subset is not achievable using a find query. When I try to filer the return object, it deletes relationships from the database!
I tried this:
class BookCategory{
String name
static hasMany = [books:Book]
}
class Book{
String title
}
def myController() {
def categories
categories = BookCategory.list()
def user = getCurrentUser()
categories.each { category ->
category.books.removeAll { book ->
!isBookBannedForThisUser(book.title, user)
}
[bookCategories: categories]
}
}
The problem is that it permanently removes these books from the categories for all users from the database!!!
I tried putting the method in a service and using a readonly transaction, but this did not help.
I assume that even if I copy all the categories and books into new list, they will still update the DB as they will still have the book IDs (which I need)
Saving to the database when you dont say save() is very dangerous. is there a way to disable this feature completely?
There is a fundamental flaw in your approach. Do not modify your domain instances if you don't intend to have the changes persisted. Doing so is going to cause you headaches.
Your domain model is suppose to be your system of record. Any changes to it are suppose to be persisted.
If you need to gather up data and manipulate it without having it reflected in your domain model then use a DTO (data transfer object) or similar pattern.
Simply calling .discard() will discard the changes you have made from being persisted when the session automatically flushes.
Instead of working against the framework, and disabling behavior, change your approach to be correct.

Removing association from M:N relationship when deleting an entity

In my domain I have a many-to-many relationship. The problem is that GORM forces me to define owner entity but I don't think that either side "owns" the relation.
class User {
String username
String password
static hasMany = [organizations: Organization]
static belongsTo = Organization
static constraints = {
}
}
class Organization {
String name;
static hasMany = [members: User]
}
In this case I'm obviously not allowed to delete User who is in some organization (because Organization "owns" the relation). I would like to be able to delete both entities and on delete simply remove the relation (row from user_organization table). Is it possible or do I have to write this logic myself (if so, what would be the best way to implement this)?
You are allowed to delete both sides of the relationship, no matter who is the "owner". The belongsTo just applies the proper cascading so you don't have to.
In your example if you want to delete a user you first have to remove the relationship. So, in order to delete a user you do:
organization.removeFromMembers(user)
user.delete()
And if you delete an organization, since it is the "owner", you just don't need to use removeFrom*.

How to define association relationship in grails

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.

Removing child-parent association in GORM

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"
}

Resources