Grails one to one on two domain object - grails

I want to add a child to two different parent class, like that:
First:
class Member {
Profile profile
Member() {
this.profile = new Profile()
}
static mapping = {
profile cascade: 'all-delete-orphan'
}
}
Second:
class Team {
Profile profile
Team() {
this.profile = new Profile()
}
static mapping = {
profile cascade: 'all-delete-orphan'
}
}
Thw child is simply define like that
class Profile() {
}
The probleme is when I save the parent, it dosent save the child:
Member member = new Member().save(flush: true, failOnError: true)
assert !member.hasErrors()
assert member.profile
assert !member.profile.hasErrors()
assert member.profile.id //FAIL
What do I do wrong? Is there a better way to do it?
UPDATE:
I found this
Saving associated domain classes in Grails
It seem that 'belong to' is needed for that kind of behavior. But Why the 'cascade: 'all-delete-orphan' doesn't force this ? Because I can't use 'belong to' in that specific case

I copied your example (with slight modification to change group table name to a non-reserved word) and the cascades are working properly using grails 2.2.1. Both Member and Group cascaded their saves to the newly created Profiles.
Assuming your classes are more complicated than this, you might have an error elsewhere in your class (eg cascade behavior described in constraints instead of mapping, etc).

I found a nice solution. The best was to define both parent in the child but nullable. Like that:
class Profile() {
static belongsTo = [member: Member, team: Team]
static constraints = {
member nullable: true
team nullable: true
}
}
This way, the cascade behavior work just fine !

Related

Can many-to-many relationships be cleaned up in a beforeDelete method?

Sample project: https://github.com/joemccall86/cascade-delete-test/tree/automatic-collection-purge
I'm not sure if this is a bug since I haven't seen an example of it working anywhere.
Say I have a the following domain classes:
class Organization {
String name
static hasMany = [
users: Person,
teams: Team
]
static mapping = {
users cascade: 'all-delete-orphan'
}
}
class Person {
String name
Organization organization
static belongsTo = [Organization, Team]
static hasMany = [teams: Team]
static constraints = {
}
def beforeDelete() {
Team.withNewSession {
removeFromAllTeams()
}
true
}
def removeFromAllTeams() {
Team.where {
members {
id == this.id
}
}.each { Team team ->
if (team.members.contains(this)) {
team.members.remove(this)
team.save()
}
}
}
}
class Team {
String name
static hasMany = [members: Person]
static belongsTo = [organization: Organization]
static constraints = {
}
}
According to https://spring.io/blog/2010/07/02/gorm-gotchas-part-2/ (specifically the section regarding many-to-many relationships cascading), I need to manually clear the persons from the team before I can perform a successful deletion. That is done with the method removeFromAllTeams.
I can call that manually every time I plan on deleting a user (which would typically happen inside a service call), but it looks more like it belongs inside a beforeDelete method on the domain object itself. However when I put it there, I get:
SQL [n/a]; Referential integrity constraint violation: "FKGHKKY8WMH379RPMFH92T807RY: PUBLIC.TEAM_MEMBERS FOREIGN KEY(PERSON_ID) REFERENCES PUBLIC.PERSON(ID) (1)"; SQL statement:
delete from person where id=? and version=? [23503-194]; nested exception is org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "FKGHKKY8WMH379RPMFH92T807RY: PUBLIC.TEAM_MEMBERS FOREIGN KEY(PERSON_ID) REFERENCES PUBLIC.PERSON(ID) (1)"; SQL statement:
delete from person where id=? and version=? [23503-194]
Here's a spock test to illustrate what I'm doing:
#Unroll
void "user is removed from team before deletion, runManually = #runManually"() {
given: 'an existing org'
Organization.withNewSession {
def organization = new Organization(name: 'Cyberdyne Systems').save(failOnError: true)
and: 'a person is added'
organization.addToUsers(name: 'John Connor').save(failOnError: true)
organization.save(failOnError: true, flush: true)
and: 'a new team is added to the org'
organization.addToTeams(name: 'IT').save(failOnError: true, flush: true)
and: 'the person is added to the team'
organization.teams.first().addToMembers(organization.users.first())
organization.save(failOnError: true, flush: true)
}
and: 'the person is deleted'
def userToDelete = Person.first()
if (runManually) {
userToDelete.removeFromAllTeams()
}
userToDelete.delete(flush: true)
expect: 'the team has no users'
Team.first().members.isEmpty()
and: 'there are no more users'
Person.count == 0
where:
runManually << [true, false]
}
What am I missing? In other words, is it even possible to have a domain class do its own relationship clean-up inside beforeDelete?
I have experienced this issue with Grails 3.2.12 and GORM 6.1.6.RELEASE.
Like you, I tried adding the clean-up code to the beforeDelete method, but it didn't work. According to my commit log:
"For some reason, this cannot be done in beforeDelete, possibly because GORM checks whether the delete is valid before calling beforeDelete".
I suggest you create an issue on GitHub and ask the Grails team to clarify whether this is a bug or a feature.

grails .removeFrom when object exists elsewhere in class

So essentially I have two classes:
Class User {
String Name
}
Class Project {
User requestedBy
static hasMany =
[
assignedTo: User
]
}
Now, I can set the requestedBy to say, User 1.
I can also do Project.addToAssignedTo(User 1).
The problem comes when I want to remove the user from assigned to when they already exist as the requestedBy. I can remove other users without problem:
Project.removeFromAssignedTo(User 1).save(failOnError: true, flush: true)
I get no errors of any kind, the data just simply does not get removed. Any help would be appreciated!
Thanks!
When defining multiple relationships to the same class, you should define the bidirectional relationship, and use the mappedBy property to define both sides of that relationship:
class User {
String Name
hasMany = [requestedProjects: Project, assignedProjects: Project]
}
class Project {
User requestedBy
static hasMany =
[
assignedTo: User
]
static mappedBy = [requestedBy: 'requestedProjects', assignedTo: 'assignedProjects']
}
Hopefully that solves your problem.

grails - using multiple belongsTo, but only one at a time

If I want to use a domain class, e.g. MoneyTransaction, for two entirely different purposes, i.e.:
1) when a customer places an order
2) when a member gets paid
such that I have something like:
class Order {
static hasMany = [transactions: MoneyTransaction]
}
class Member {
static hasMany = [payments: MoneyTransaction]
}
and
class MoneyTransaction {
static belongsTo = [order: Order, member: Member]
static constraints = {
order(nullable: true)
member(nullable: true)
}
}
and then in essence only use one belongsTo/association at a time, is this pretty "standard" usage, or do I need to switch this modeling? Right now MoneyTransaction has both credit card and ACH payment capabilities, as both apply for orders. For payments, just the ACH portion will be used.
The domain class definitions that you have posted seem correct based on your requirements. One modification that I'd make here would be to add a custom validator to make sure that both order and member are not null at the same time.
static constraints = {
order(nullable: true, validator: {field, inst -> inst.member || field})
member(nullable: true)
}

Grails GORM self-referential belongsTo deletes opposite direction from expected

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

Grails/GORM: What is the correct/best way to map these two domains?

I have a User domain class, and a List one.
Each list must have an author (a user) and each user must have a "primary list". Only some of the lists will have the "primaryList" statute.
So.. somthing like
User:
List primaryList
List:
User author
static belongsTo = User
Of course this does not work as intended because the two relations are mistakenly taken as only one. I should also add a hasMany on the User and other belongsTo to the List..., but I don't want to complicate the example because I want to get the right answer from you.
You may need to use mappedBy to explain how the fields in User and List line up. Here are a couple domains that I wrote that allow a User to author many Lists but only set one to be "primary". There are a couple extra nullable constraints so you can use the scaffolded UI without getting into a chicken-and-egg scenario.
class User {
String name
FooList primaryList
static hasMany = [authoredLists: FooList]
static mappedBy = [primaryList: 'primaryOwner', authoredLists: 'author']
static constraints = {
primaryList nullable: true, unique: true
authoredLists nullable: true
}
String toString() { name }
}
I named this class "FooList" just to avoid confusion with the standard List class:
class FooList {
static belongsTo = [author: User, primaryOwner: User]
static constraints = {
primaryOwner nullable: true, display: false
}
}
Try using the map belongsTo approach:
static belongsTo = [user:User]
That way Grails should see the 2 properties as separate.

Resources