I want to do reflexion on Domain associations to automatically generate a JSON/XML format class descriptor file for a RESTful WS (scaffolding). In the case of HasOne (supposedly bidirectional), I am trying to use the referencedPropertyName in the Association object to show the reverse key (that's the only information that the WS has to show).
Nevertheless I find that hasOne Associations are not correctly initialized.
What follows is the test I made using Grails 2.3.7.
My domains are:
class House {
Long number
Integer inhabitants
static hasOne = [ roof: Roof ]
static hasMany = [ doors: Door ]
static constraints = {
roof nullable: true, unique: true
}
}
class Roof {
String color
House house
static constraints = {
}
}
Then:
import org.grails.datastore.mapping.model.types.Association
import org.junit.Test
import spock.lang.*
import test.House
class AssociationsSpec {
#Test
void "test something"() {
House.gormPersistentEntity.associations.each { Association association ->
String key = association.name
println association.properties
assert association.bidirectional
}
}
}
This test fails:
Assertion failed:
assert association.bidirectional
| |
| false
test.House->roof
at test.AssociationsSpec$_test_something_closure1.doCall(AssociationsSpec.groovy:26)
at test.AssociationsSpec.test something(AssociationsSpec.groovy:23)
Additional info (println association.properties):
[list:false, type:interface java.util.Set, owner:org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity#149a797, class:class org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity$5, cascadeOperations:[ALL], inverseSide:test.Door->house, capitilizedName:Doors, bidirectional:true, referencedPropertyName:house, associatedEntity:org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity#99a9c3, mapping:null, circular:false, owningSide:true, name:doors, fetchStrategy:EAGER]
[capitilizedName:Roof, bidirectional:false, referencedPropertyName:null, circular:false, owningSide:true, name:roof, list:false, type:class test.Roof, owner:org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity#149a797, class:class org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity$4, cascadeOperations:[ALL], inverseSide:null, associatedEntity:org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity#16289cc, mapping:null, foreignKeyInChild:true, fetchStrategy:EAGER]
Thank you
In order to make this bidirectional you need to add static belongsTo = [house: House] and remove your House house property from the Roof domain.
You can read more about the belongsTo property in the documentation.
The problem was that inverse side wasn't initialized.
Fixed in: https://github.com/grails/grails-data-mapping/commit/3c6b7f296be359742062c26113d2ce5c617167c7
Related
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?
What I want: when I call validate (or save) on a parent domain during a unit test, it will check the constraints of all children domains AND their children AND their children etc.
What is happening: during a unit test for a service, validate keeps returning true despite constraints not being met for children domains. Please help...
I had two change two things about my code:
1) I had to make sure all domains were mocked for the unit test.
2) I had to make sure each child domain's back reference to the parent was populated. obj.addToSomeProperty(property) automatically populated back references for one-to-many objects, but for one-to-one I had to set it manually.
Service Unit Test:
#TestFor(GardenerService)
#Mock([Orchard, Plot, Tree, Fruit])
class GardenerServiceSpec extends Specification {
void "test save and fetch"() {
setup:
Fruit fruit = new Fruit(name:"Peach")
Tree tree = new Tree(height:6)
tree.addToFruits(fruit)
Plot plot = new Plot(litersOfWaterAdded:10)
plot.tree = tree // Must be a better way... why didn't setTree(tree)
tree.plot = plot // set the back reference
Orchard orchard = new Orchard(orchardName:"Eden")
orchard.addToPlots(plot)
when:
orchard.save(flush: true)
then:
orchard.validate()
Fruit.findByName("Peach").tree.plot.orchard.name == "Eden"
Tree.findByHeight(6).plot.litersOfWaterAdded == 10
}
}
Domains:
class Orchard {
String orchardName
static hasMany = [plots: Plot]
}
class Plot{
static belongsTo = [orchard: Orchard]
int litersOfWaterAdded
static hasOne = [tree: Tree]
}
class Tree {
static belongsTo = [plot: Plot]
int height
static hasMany = [fruits: Fruit]
}
class Fruit {
static belongsTo = [tree: Tree]
String name
String description
static constraints = { description nullable: true }
}
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]
}
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 a newbie in grails. i'm having a problem right now in my domain classes. I have 3 domain classes, class Patient,class Nurse and class NursePatient, the class NursePatient is a composite key where you can see who is the attending Nurse in a Patient, so if you view its table you can only see the id's of nurses and patients. This is my code for Nurse class:
class Nurse {
String name
Nurse partner
boolean idle = true
static belongsTo = [hospital: Hospital]
static constraints = {
name(blank:false)
partner(nullable:true)
hospital(nullable:false)
}
String toString(){
"Nurse ${name}"
}
}
--> and this is my domain class for NursePatient:
class NursePatient implements Serializable{
Nurse nurse
Patient patient
static mapping = {
version false
id composite:['nurse', 'patient']
}
static constraints = {
patient(nullable:false, validator:{val, obj -> val.hospital == obj.nurse.hospital})
nurse(nullable:false)
}
String toString(){
"Nurse ${nurse.name} - ${patient.name}"
}
void saveIt(Nurse x, Patient y){
def np = new NursePatient(nurse: x, patient: y)
if(np.save()){
def n = nurse.get(nurse.id)
n.idle = false
}
}
}
--> I was asked to print a list of nurses who doesn't have a patient. I was thinking that the moment I save in table using the saveIt() method from class NursePatient, once the save() is successful it changes the value of the property idle of class Nurse from true to false so that querying is much more easier. My problem is I don't if my code in class NursePatient is correct or is it possible to change the value of a property from another class. Please Help me.. thank You!!
Changing properties of domain classes inside different classes is fine.
However, you don't really need a NursePatient class. If you declare the relationship between Nurses and Patients as many-to-many, like this:
class Nurse {
static hasMany = [patients: Patient]
...
}
class Patient {
static hasMany = [nurses: Nurse]
...
}
then Grails will create and update the needed join table automatically. You can then query for all the nurses without patients using Criteria API:
def nursesWithoutPatients = Nurse.withCriteria { isEmpty("patients") }