Domain classes to track Users and Games - grails

I'm trying to do something that seems so simple. I have a User class, and I have a Game class that matches up two Users. It's this simple:
class User {
String username
static hasMany = [games:Game]
}
class Game {
User player1
User player2
}
When I ran this, I got
Caused by GrailsDomainException: Property [games] in class [class User] is a bidirectional one-to-many with two possible properties on the inverse side. Either name one of the properties on other side of the relationship [user] or use the 'mappedBy' static to define the property that the relationship is mapped with. Example: static mappedBy = [games:'myprop']
So I did some digging and found mappedBy and changed my code to:
class User {
String username
static hasMany = [games:Game]
static mappedBy = [games:'gid']
}
class Game {
User player1
User player2
static mapping = {
id generator:'identity', name:'gid'
}
}
Now I get
Non-existent mapping property [gid] specified for property [games] in class [class User]
What am I doing wrong?

So here is probably what you want:
class User {
static mappedBy = [player1Games: 'player1', player2Games: 'player2']
static hasMany = [player1Games: Game, player2Games: Game]
static belongsTo = Game
}
class Game {
User player1
User player2
}
Edit for new rules:
class User {
static hasMany = [ games: Game ]
static belongsTo = Game
}
class Game {
static hasMany = [ players: User ]
static constraints = {
players(maxSize: 2)
}
}

Related

Multiple Self-referencing relationships

I have an Abstract Class which all DomainClasses are extending.
This Abstracte Class looks like this:
abstract class DomainBase {
Date created = new Date(), modified = new Date()
User createdBy, modifiedBy
int dataStatus = 30
}
My DomainClass User also extends the abstract Class and has multiple self-referencing relationship:
User principal
static hasMany = [employees: User, skills: UserSkill,...]
static mappedBy = [employees: 'none' ]
UserSkill:
class UserSkill extends DomainBase {
String category
String name
static belongsTo = [User]
static hasMany = [users: User]
static mappedBy = [users: 'skills']
static mapping = {
table 'skill'
users column: 'skill_id', joinTable: 'user_skills'
}
}
With that i'm getting an MappingException:
nested exception is org.hibernate.MappingException: broken column mapping for:
createdBy.skills of: de.streit.user.UserSkill
How do I mapp the classes correctly?
Stepping away from Grails for a moment... you've got an object-oriented design problem. According to your design, DomainBase sits at the top of your hierarchy. Because of this DomainBase should not depend on its subclasses. Here's why:
According to the Liskov substitution principle, if class B extends from class A, then an instance of class B should be usable wherever an instance of class A is expected.
For example, if class Duck extends class Bird, I can honestly say a Duck is a Bird. My nose would not get any longer. If a method expects a Bird and I give it a Duck the method won't know the difference.
In your case, a User cannot be a DomainClass because a DomainClass has a user. Can a Bird have a Duck? Nope. A Bird should not know anything about Ducks. Animals aside, your class hierarchy violates this principle. But this can be fixed :)
Solution
As long as you're using Groovy 2.3 or grater, a Groovy trait can address your issue.
First, create a trait somewhere in grails-app/src/main/groovy/. It's best if you place it in the same Groovy (Java) package as your domain classes.
package xzy
trait Auditable {
Date created = new Date(), modified = new Date()
User createdBy, modifiedBy
int dataStatus = 30
}
Then, have your domain classes implement the trait.
package xyz
class User implements Auditable {
User principal
static hasMany = [employees: User, skills: UserSkill,...]
static mappedBy = [employees: 'none' ]
}
class UserSkill implements Auditable {
String category
String name
static belongsTo = [User]
static hasMany = [users: User]
static mappedBy = [users: 'skills']
static mapping = {
table 'skill'
users column: 'skill_id', joinTable: 'user_skills'
}
}
This works because your domain classes will magically get the properties defined in the trait (created, createBy, and dataStatus) without baggage of inheritance. Plus, if a method expects an Audiable, you can pass it a User or UserSkill and the method wouldn't know the difference.
Watch this: Users and UserSkills are Auditable. Makes sense huh?

grails: multiple belongsTo with back reference

Is it possible to have a domain class that belongs to multiple domain classes with back reference? For instance:
class Person {
List<Book> books
static hasMany = [books: Book]
}
class Organization {
List<Books> books
static hasMany = [books: Book]
}
class Book {
def owner // what's the type?
static belongsTo = [Person, Books]
}
A Book can belong to a Person or an Organization, but not both.
Person and Organization have separate sequence IDs.
The solution I came up with is:
class Book {
Long ownerID
String ownerClass
static belongsTo = [Person, Books]
static transients = ['owner']
static constraints = {
ownerId(nullable:false, blank:false)
ownerClass(nullable:false, blank:false)
}
public BookOwner getOwner() {
grailsApplication.getArtefact("Domain", ownerClass)?.getClazz()?.get(ownerId)
}
}
where BookOwner is an Interface implemented by Person and Organization. So calling a bookInstance.owner will return a Person or Organization instance, both BookOwner.
My solution works well, but it doesn't feel right - a sure sign that I am not fully understanding what I'm doing. What's the best way to implement this? Should I completely give up on having the extremely convenient back reference?
Thank you
I guess, you should have made Owner superclass. Grails will create Owner table with field class meaning child class names (in your case: Person, Organization).

How to implement Self-Referencing Relationships in Grails?

Given the following User class:
class User {
String name
static hasMany = [friends: User]
}
I want that a User can have many friends which are instances of the user domain class.
How do I have to implement the friend relationship of a user?
1. How Do you Define the relathionship
class User {
static hasMany = [ friends: User ]
static mappedBy = [ friends: 'friends' ] //this how you refer to it using GORM as well as Database
String name
String toString() {
name
}
def static constrains () {
name(nullable:false,required:true)
}
def static mapping={
/ / further database custom mappings ,like custom ID field/generation
}
}
2.How to save Data:
def init = {servletContext->
if(User?.list()==null) { // you need to import User class :)
def user = new User(name:"danielad")
def friends= new User(name:'confile')
def friends2=new User(name:'stackoverflow.com')
user.addToFriends(friends)
user.addToFriends(friends2)
user.save(flash:true)
}
}
3# . Your question is repeated on this stack overflow link :
Maintaining both sides of self-referential many-to-many relationship in Grails domain object
It looks like many-to-many relationship (one user has a lot of friends, and is a friend of a lot of users). So one of the solution will be to create new domain class, lets say it Frendship. And then modify User domain class like here:
class Friendship {
belongsTo = [
friend1: User
, friend2: User
]
}
class User{
String name
hasMany = [
hasFriends: Friendship
, isFriendOf: Friendship
]
static mappedBy = [
hasFriends: 'friend1'
, isFriendOf: 'frined2'
]
}

No owner defined between domain classes: many to many relationship

These are my domain classes:
class Game {
static hasMany = [players: User]
static belongsTo = [owner: User]
}
class User {
static hasMany = [games: Game]
}
If I try to use them as they are I get No owner defined between domain classes. So I need to set the owner of the relationship. Adding static belongsTo = Game to User causes Domain classes cannot own each other in a many-to-many relationship.
The only other option I can think of is to add static belongsTo = User to the Game class but I already have a belongsTo there.
How do I model this?
class Game {
User owner
static hasMany = [players: User]
static belongsTo = User
}
class User {
static hasMany = [games: Game]
}
You will have to specify one side of the relationship, as the owner, by doing this you will make User domain class as the owner of the many to many relationship.
The belongsTo field controls where the dynamic addTo*() methods can be used
from. we’re able to call User.addToGames() because Game belongsTo
User. we can’t call Game.addToPlayers().
Try this:
class Game {
User owner
static hasMany = [players: User]
static belongsTo = User
}
maybe it would be better not to use "belongsTo" at all? I mean using simple field reference like
class Game {
User owner
static hasMany = [players: User]
}
FYI,
I would be careful with using "owner" field name, it might cause problems when used somehow in a closure which has its own "owner" reference (e.g. in criteria builder)
I know it caused problems back in grails 1.3.X, I don't know if it's still the case
try this:
class Game {
User owner
static hasMany = [players: User]
static belongsTo = User
}
class User {
static hasMany = [games: Game, owns: Game]
static mappedBy = [games:'players', owns: 'owner']
}
this settings work for me. And here is a good thread for mappedBy: GORM mappedBy and mapping difference

Grails many to many using a third 'join' class

I read that a m:m relationship often means there is a third class that isn't yet required. So I have m:m on User and Project, and I created a third domain class, ProjectMembership
The three domains are as follows (minimized for illustration purposes):
User
class User {
String name
static hasMany = [projectMemberships : ProjectMembership]
}
Project Membership
class ProjectMembership {
static constraints = {
}
static belongsTo = [user:User, project:Project]
}
Project:
class Project {
String name
static hasMany = [projectMemberships : ProjectMembership]
static constraints = {
}
}
If I have the ID of the user, how can I get a list of Project objects that they are assigned to?
There are a handful of ways - here are a couple:
def user = User.get(userId)
ProjectMembership.findAllByUser(user).collect { it.project }
or to avoid the query for the User:
ProjectMembership.withCriteria {
user {
eq('id', userId)
}
}.collect { it.project }
Be wary of queries that'll return large result sets - you'll end up with a huge in-memory list of project objects.

Resources