Can we have relation like - Grails hasMany-belongsTo and instance variable - grails

Class A
{
static constraints = {
bObj(nullable:true)
}
B bObj
static hasMany = [listB:B]
}
Class B
{
static constraints = {
aObj(nullable:true)
}
A aObj
}
Problem that Im facing is, when I retrive instance of A(whereas bObj.aObj is null) & call save on it, automatically adds reference of A into bObj.aObj, which was null before calling save().
Any thoughts on why would it happen.

Related

Grails - How do I get the value of a domain entity prop in a function of its parent class

I have an abstract class in a groovy file:
Implementation 1
public abstract class Item {
public String testStr;
public String getBigTestStr(){
String s = "___" + this.testStr;
return s;
}
}
Which is inherited by
class Material extends Item {
public String testStr;
static marshalling = {
detail {
includes "bigTestStr"
}
summary {
includes "bigTestStr"
}
}
static mapping = {
table 'materialset'
id column: 'NODEID'
testStr column: 'MATERIALTYPE'
version false
}
}
The idea is that hitting the endpoint for a material will return the return value of Item.bigTestStr(). However, when I trace through Item.bigTestStr(), the debug's variables table shows a value for this.testStr, but is null when it is added to s. See here:
I tried taking the testStr property out of Material
Implementation 2
class Material extends Item {
static marshalling = {
detail {
includes "bigTestStr"
}
summary {
includes "bigTestStr"
}
}
static mapping = {
table 'materialset'
id column: 'NODEID'
testStr column: 'MATERIALTYPE'
version false
}
}
but I still get the same problem.
For both implementations the endpoint returns
{
bigTestStr: ____null
}
How can I get the actual value of Material.testStr to be used by the function in its parent class?
UPDATE
As Emmanuel pointed out, Implementation 2 is the right way to use properties from a parent class. However, this implementation does not seem to work with mapping the parent class' properties to a database column. So the real question is: How can I get Material.testStr to map to a database column?
It looks like your problem is in how you initialized your Material instance. Here's an example:
public abstract class Item {
public String testStr
public String getBigTestStr(){
"___$testStr"
}
}
class MaterialA extends Item {
public String testStr
static marshalling = {
detail {
includes 'bigTestStr'
}
summary {
includes 'bigTestStr'
}
}
static mapping = {
table 'materialset'
id column: 'NODEID'
testStr column: 'MATERIALTYPE'
version false
}
}
class MaterialB extends Item {
static marshalling = {
detail {
includes 'bigTestStr'
}
summary {
includes 'bigTestStr'
}
}
static mapping = {
table 'materialset'
id column: 'NODEID'
testStr column: 'MATERIALTYPE'
version false
}
}
Shown above are three classes Item, MaterialA, and MaterialB. The two material classes simulate your two tests: MaterialA has a testStr property, while MaterialB inherits a property with the same name from Item instead. Here's what happens when instances of both classes are initialized and getBigTestStr() is tested:
new MaterialA(testStr: 'Hello').with {
assert bigTestStr == '___null'
}
new MaterialB(testStr: 'Hello').with {
assert bigTestStr == '___Hello'
}
In short, your second approach, inheriting the property, works. A super class does not (and should not) have access to anything in its subclasses. It doesn't even know about its subclasses. The approach works because initializing testStr in an instance of MaterialB actually initializes the inherited property from Item; which of course is accessible within the Item class.
In your case, Grails is initializing the instances for you using the values stored in the database. So I'd check your database.
Update
Here's an example using a trait rather than an abstract class:
public trait Item {
String testStr
public String getBigTestStr(){
"___$testStr"
}
}
class Material implements Item {
static marshalling = {
detail {
includes 'bigTestStr'
}
summary {
includes 'bigTestStr'
}
}
static mapping = {
table 'materialset'
id column: 'NODEID'
testStr column: 'MATERIALTYPE'
version false
}
}
new Material(testStr: 'Hello').with {
assert bigTestStr == '___Hello'
}
This makes it so that there's no need for an Item table.

Grails - Validation of embedded object attributes only while updating

I have a grails domain class with an embedded object, I want to validate the embedded object's attributes only while updating.
I know I can do this on a normal grails domain class by using a custom validator and checking if the domain's class id is not null.
I can't do this because of the lack of an id on an embedded object.
There is a little example of what I want to do.
//This is on domain/somePackage/A.groovy
class A{
B embeddedObject
static embedded = ['embeddedObject']
static constraints = {
embeddedObject.attribute validator:{val, obj-> //The app fails to start when this code is added!!
if(obj.id && !val) //if the id is not null it means the object it's updating...
return 'some.error.code'
}
}
}
//this class is on src/groovy/somePackage/B.groovy
class B{
String attribute
static constraints={
attribute validator:{val,obj->
if(obj.id && !val) //This will fail too! because the lack of an id on this object....
return 'some.error.code'
}
}
}
Is there a way to get the id of the 'parent' on the embedded object??
Any help will be appreciated
way too complicated:
class A{
B embeddedObject
static embedded = ['embeddedObject']
static constraints = {
embeddedObject validator:{ val, obj ->
if( obj.id && !val.attribute )
return 'some.error.code'
}
}
}

Inherited grails domain classes missing dynamic properties

I'm having a problem where the related table id fields return 'null' from my domain objects when using inheritance. Here is an example:
In /src/groovy/
BaseClass1.groovy
class BaseClass1 {
Long id
static mapping = {
tablePerConcreteClass true
}
}
BaseClass2.groovy
class BaseClass2 extends BaseClass1 {
String someOtherProperty
static constraints = {
someOtherProperty(maxSize:200)
}
static mapping = BaseClass1.mapping
}
In /grails-app/domain
ParentClass.groovy
class ParentClass extends BaseClass2 {
ChildClass myChild
static mapping = BaseClass2.mapping << {
version false
}
}
ChildClass.groovy
class ChildClass extends BaseClass1 {
String property
static mapping = BaseClass1.mapping
}
The problem appears here:
SomeotherCode.groovy
print parentClassInstance.myChild.id // returns the value
print parentClassInstance.myChildId // returns null
Any ideas what might be going on to get those dynamic properties to break like this?
After debugging into the get(AssociationName)Id source, I found the following:
The handler for this is:
GrailsDomainConfigurationUtil.getAssociationIdentifier(Object target, String propertyName,
GrailsDomainClass referencedDomainClass) {
String getterName = GrailsClassUtils.getGetterName(propertyName);
try {
Method m = target.getClass().getMethod(getterName, EMPTY_CLASS_ARRAY);
Object value = m.invoke(target);
if (value != null && referencedDomainClass != null) {
String identifierGetter = GrailsClassUtils.getGetterName(referencedDomainClass.getIdentifier().getName());
m = value.getClass().getDeclaredMethod(identifierGetter, EMPTY_CLASS_ARRAY);
return (Serializable)m.invoke(value);
}
}
catch (NoSuchMethodException e) {
// ignore
}
catch (IllegalAccessException e) {
// ignore
}
catch (InvocationTargetException e) {
// ignore
}
return null;
}
It threw an exception on the related class (value.getClass().getDeclaredMethod), saying NoSuchMethod for the method getId(). I was unable to remove the id declaration from the base class without Grails complaining that an identifier column was required. I tried marking id as public and it also complained that it wasn't there. So, I tried this
BaseClass {
Long id
public Long getId() { return this.#id }
}
and things worked on some classes, but not on others.
When I removed the ID declaration, I go an error: "Identity property not found, but required in domain class". On a whim, I tried adding #Entity to the concrete classes and viola! everything started working.
class BaseClass {
//Don't declare id!
}
#Entity
class ParentClass {}
#Entity
class ChildClass {}
I still think it is a grails bug that it needs to be added, but at least it is easy enough to work around.
I'm not sure why you are seeing this behavior, but I'm also not sure why you are doing some of the things you are doing here. Why have a domain class extend a POGO? Domains, Controllers, and Services are heavily managed by the Grails machinery, which probably was not designed for this sort of use. Specifically, I believe Grails builds the dynamic property getters for the GrailsDomainProperty(s) of GrailsDomainClass(es), not POGO's. In this case, you have an explicitly declared id field in BaseClass1 that is not a GrailsDomainProperty. I suspect that this POGO id property is not picked up by the Grails machinery that creates the dynamic property getters for Domains.
You might try putting BaseClass1/2 in /grails-app/domain, perhaps making them abstract if you don't want them instantiated, then extending them as you are and seeing if you observe the behavior you want.

Import domain in constraints

I have 2 domain classes
class a {
String name
static constraints = {
name unique:true
}
}
class b {
String description
}
and in domain class b I want to call class a
import a
class b {
String description
static constraints = {
description unique:'a.name'
}
}
and get error
Scope for constraint [unique] of property [description] of class [b] must be a valid property name of same class
How do I get a property from class a to b?
Assuming you try to do this in Grails 2+
You can't use validation that way. In your example you need to reference to a property of the same domain class. To correct the constraint in class B you can write:
class B {
String description
static contraints = {
description unique:true
}
}
But I think you want to import the constraints from class a which is done like this.
class B {
String description
static contraints = {
importFrom A
}
}
See http://grails.org/doc/latest/guide/validation.html#sharingConstraints
This will import all constraints on properties that the two classes share. Which in your case is none.
UPDATE
I got a similar question and found a solution for it. So I thought to share it here with you.
The problem can be solved with a custom validator. In your case constraints for class B:
static constraints = {
description(validator: {
if (!it) {
// validates to TRUE if the collection is empty
// prevents NULL exception
return true
}
def names = A.findAll()*.name
return names == names.unique()
})
}
It's difficult to answer your question correctly since the requirements are a bit odd. But maybe it will help.
You need to write a custom validator to check the uniqueness since it relies on more information than the single instance of b will have.
Something like
static constraints {
description validator: { val ->
!a.findByName(val)
}
}
might do the trick.

How can I make grails accept a belongsTo relation that can be null?

What I am trying to do is have two domains that reference each other. However one does not necessarily have the other or belongs to the other. Each object from both domains can reference 0 or 1 object from the other domain.
I have this code, but it doesnt work:
class Domain1{
//declare some vars
...
static belongsTo = [domain2Object:Domain2]
static constraints = {
domain2Object(nullable:true)
}
}
Using hasOne with the nullable:true constrain works, but it doesnt work if the other side has the same thing. The point is that I want to be able to delete any object from any of the domains that is referring an object from the another domain without causing the referred object to be deleted as well. So like I said, no object belongs to the other, they just reference each other.
=========================================================================================
Response:
using this in both domain classes:
class ClassB {
static hasOne = [classA:ClassA]
def beforeDelete = {
classA?.delete()
}
}
static constraints = {
classA(nullable:true)
}
I get this error when i try to add an object of any of the two classes leaving the other class blank:
"Integrity constraint violation - no parent FK24742AC1AA048190 table: PENDINGORDER"
You can use "hasOne" onDelete event.
class ClassB {
static hasOne = [classA:ClassA]
def beforeDelete = {
classA?.delete()
}
}
Events and Auto Timestamping
I guess the exception happens because you are trying to delete an object by it's relation in the beforeDelete event. Remove your relation before deleting it like this :
class ClassA {
ClassB classB
static constraints = {
classB nullable: true
}
}
class ClassB {
ClassA classA
static constraints = {
classA nullable: true
}
def beforeDelete = {
classA?.classB? = null
classA?.delete(flush:true)
}
}

Resources