I have three classes in my grails project. What is the proper grails domain definition
Class A {
List<Resource> xResources
List<Resource> yResources
hasMany = [ xResources: Resource, yResources: Resource]
}
Class B {
List<Resource> zResources
hasMany = [ zResources: Resource]
}
Class Resource {
String title
.....
..
belongsTo = [A, B]
}
The above definition fails because i have not mentioned the mappedBy in Class A. How that can be avoided. I want the Resource class to be generic.
I don't want to restrict the Resource class only to the two classes, but i should allow it extend it to other. I also need to get the source reference from a Resource object. What should be proper GORM definition for this scenario.?
I see 3 major issues in your code:
The properties xResources, yResources, zResources is double defined. Just remove the duplicate List<Resource> ... definition.
hasMany and belongsTo should have a static key word before it.
I'm not sure belongsTo can point to more than one class. If not, just remove it.
Please follow the below code
Class A {
static hasMany = [ xResources: Resource, yResources: Resource]
}
Class B {
static hasMany = [ zResources: Resource]
}
Class Resource {
String title
.....
..
static belongsTo = [a:A, b:B]
}
Related
What is the difference between
static belongsTo [author:Author]
and
static belongsTo = Author
Lets consider two domain class.
Class Author{
String name
}
Class Books{
String name
static belongsTo = Author
}
When static belongsTo = Author is kept in Books domain, it have no effects on db. However, static belongsTo = [author : Author] creates a backreference to Author class and also there is author_id column in db. So, what actually using static belongsTo = Author alone does.
This is expalined in grails docs(http://grails.github.io/grails-doc/latest/ref/Domain%20Classes/belongsTo.html).
Also, what is the difference between using following two :
Class Books{
String name
static belongsTo = [author : Author]
}
Class Books{
String name
Author author
}
static belongsTo = [author : Author] is used only for cascading purposes, Is it true or does it have different use cases.
Can anyone explain these in detail without relating it to hasOne or hasMany. Thanks in advance.
Using belongsTo without a back-reference is necessary for many-to-many associations. For example, lets assume you have the following domain classes:
class Book {
String name
Author author
static hasMany = [categories: Category]
}
class Category {
String name
static hasMany = [books: Book]
}
If you try to use them as-is you'd get an exception like this:
No owner defined between domain classes [class Book] and [class Category] in a many-to-many relationship. Example: static belongsTo = Category
The solution is to make one of the domain classes the owner of the many-to-many association. Using my example, I think it makes more sense to make the Category the owner. However, a back-reference would not work because there could be multiple Categorys. So, this is where a belongsTo without a back-reference comes in:
class Book {
String name
Author author
static hasMany = [categories: Category]
static belongsTo = Category
}
If you use static belongsTo = [author: Author] then a property named author of type Author is added to the class as a back reference. With static belongsTo = Author that does not happen.
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?
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).
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.
I'm trying to do a many-to-many relationship on Grails 1.3.4 and I'm getting this exception:
Caused by: org.codehaus.groovy.grails.exceptions.GrailsDomainException: No owner defined between domain
classes [class gblog.Post] and [class gblog.Comentario] in a many-to-many relationship.
Example: static belongsTo = gblog.Comentario
The code for Comentario is:
package gblog
class Comentario {
static constraints = {
}
String conteudo
Date data
static belongsTo = [post:Post, autor:Usuario]
static hasMany = [posts:Post]
}
The code for Post is:
package gblog
class Post {
static constraints = {
}
String titulo
String conteudo
String palavrasChave
Date data
static belongsTo = [categoria:Categoria, autor:Usuario]
static hasMany = [comentarios:Comentario]
}
Thanks everybody!
I think Grails is getting confused here:
static belongsTo = [post:Post, autor: Usuario]
static hasMany = [posts:Post]
You might want to diagram how all of the classes are interacting, because I'm thinking that this is a little off.