The following is not working for me when using abstract (or non-abstract for that matter) inheritance in Grails.
Very quickly, my inheritance is as follows:
abstract BaseClass { ... }
SomeClass extends BaseClass { ... }
SomeOtherClass extends BaseClass { ... }
And then in another domain object:
ThirdClass {
...
BaseClass baseProperty
...
}
But now, when I try to set that property to either a SomeClass or SomeOtherClass instance, Grails compains:
ERROR util.JDBCExceptionReporter - Cannot add or update a child row: a foreign key constraint fails
...
Is this not possible?
I have also tried having the base class not be abstract, and also tried casting the SomeClass or SomeOtherClass instances to BaseClass. They generate the same error.
UPDATE
I just checked. It works for the first sub-class that I add. But as soon as I try to add the other sub-class it fails.
In other words:
def prop1 = new ThirdClass(baseProperty: instanceOfSomeClass).save()
works fine. But when I then try and do:
def prop2 = new ThridClass(baseProperty: instanceOfSomeOtherClass).save()
it fails.
UPDATE 2
Further investigation shows that something goes wrong during the table creation process. It correctly adds two foreign keys to the ThirdClass table, but the keys incorrectly references:
CONSTRAINT `...` FOREIGN KEY (`some_id`) REFERENCES `base_class` (`id`),
CONSTRAINT `...` FOREIGN KEY (`some_id`) REFERENCES `some_class` (`id`)
Don't know why it's choosing the base class and one of the sub-classes? I have tried cleaning etc.
First of all, create your BaseClass outside domain structure. It must be an external class, put it on script folder, source folder.
package com.example.model
/**
* #author Inocencio
*/
class BaseClass {
Date createdAt = new Date()
}
Now, create a regular domain class and extend it.
package com.example.domain
import com.example.model.BaseClass
class SomeClass extends BaseClass {
String name
static constraints = {
name(nullable: false)
}
}
As you can see, once you persist SomeClass a createdAt field is filled and saved as well. Check the test class out:
#TestFor(SomeClass)
class SomeClassTests {
void testSomething() {
def some = new SomeClass()
some.name = "Hello There"
some.save()
//find it
def someFind = SomeClass.list()[0]
assert someFind
assertTrue someFind.createdAt != null
// println "Date: $someFind.createdAt"
// println "Name: $someFind.name"
}
}
I hope it can be helpful.
I have just created class structure as yours (Grails 2.1.0) and there is no problem. It works when mocked and unit-tested. The same when scaffolded and SomeClass and ThirdClass instances saved from forms.
Try clean your DB, especially if you haven't used 'create-drop' mode. Maybe there is some old constraint left.
Last thing, you haven't specified when the error occurs - on save (create or update)? It's rather not probable to get JDBC exception on property set, is it?
I don't remember for sure but it's possible that simple property isn't cascaded by default then try to save SomeClass instance before saving the ThirdClass instance. Also you can auto-cascade instead of declaring simple property by use hasOne relation like:
class ThirdClass {
...
static hasOne = [baseProperty:BaseClass]
}
Related
I'm pretty much certain I'm doing something wrong since this obviously works. Simplified classes:
class Person {
String name
static hasMany = [cats:Cat]
}
class Cat {
String name
Person person
static belongsTo = Person
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
Simple stuff, a person has many cats, cats must belong to only a single person.
Now when I do the following in a Service class, I get strange results:
delete(Cat cat) {
Person owner = cat.person
log.debug("Cats before removing ${cat} (id=${cat.id}): ${owner.cats} -- ${owner.cats*.id}")
owner.removeFromCats(cat);
log.debug("Removed from owner ${owner}, owner now has ${owner.cats} -- ${owner.cats*.id}")
log.debug("Cat to delete is now: ${cat} and belongs to... ${cat.person}")
cat.delete(flush:true)
}
And the error is "object would be resaved, blah blah"
org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)
The weird bit is the debug results, when called to remove cat "Fluffy" who's owned by "Bob":
Cats before removing Bob-Fluffy (id=1356): [Bob-Fluffy] -- [1356]
Removed from owner Bob, owner now has [null-Fluffy] -- [1356]
Cat to delete is now: null-Fluffy and belongs to... null
What's going on that "removeFrom" isn't actually removing the object from the collection? I cleaned and recompiled. Pretty much at a loss as to why I can't delete this object.
I would try to remove the field person as Person person and leave only the belongsTo field like this
class Cat {
String name
static belongsTo = [person:Person]
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
It looks like what was happening in my case is that cat.person has getting stale somehow, even though it's the first thing in the method. Calling cat.refresh() didn't work, but calling owner.refresh() after extracting it from the cat.
I would change the domain class as such.
class Person {
String name
static hasMany = [cats:Cat]
}
class Cat {
String name
Person person
// no need to add belongs to property here. it creates a join table that you may not need
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
In the service class
delete(Cat cat) {
cat.delete(flush:true)
}
Once you make the domain changes, start with a fresh database since the schema will change.
I think that should solve your problem.
In my Grails project I need to add a particular constraint to an entry of my domain class object.
Domain class is as follows:
class HealthServiceType {
String healthService;
static belongsTo = [doctor:Doctor]
static constraints = {
}
static mapping = {
}
}
I need that healthService is not empty and is unique for each Doctor; that is, I can have multiple "test" value for healthService, but each one need to have different Doctor value.
Is it possible to perform this constraint in domain class? Or do I need to implement some check in Controller?
That's quite simple to make a unique property. For example:
static constraints = {
healthService(unique: ['doctor'])
}
The above will ensure that the value of healthService is unique for each value of doctor within your domain class.
Is there any other generic approach to receving a callback, when an object is added to a hasMany-relationship (like addTo or removeFrom is called)? Unfortunately beforeValidate is not called when an object is added or removed or at least the property hasManyXYs is not dirty if an object is removed or added. I need to do something, if an object is added or removed from a ABC.
class ABC {
List hasManyXYs
static hasMany = [hasManyXYs: XY]
}
I tried to add a constraint to the list:
class ABC {
List hasManyXYs
static hasMany = [hasManyXYs: XY]
static constraints = {
hasManyXYs(validator: {
true
}
}
}
But at least as of grails version 2.0.5, the server does not start and gives an error like this:
No such property: hasManyXYs for class: XY
Possible solutions: hasManyXYs. Stacktrace follows:
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.
I'm having trouble getting saves cascaded down my object hierarchy. Below is the code of my object hierarchy.
class Entity {
static hasMany = [attributes: Attribute]
}
class Attribute extends ValuePossessor {
static belongsTo = Entity
}
abstract class ValuePossessor {
def valueService
Value value
void setValue(val) {
this.value = valueService.Create(val)
this.value.possessor = this
}
}
abstract class Value {
static belongsTo = [possessor: ValuePossessor]
}
class StringValue extends Value {
String value
}
The valueService is simply a service with a big switch statement that creates the correct value type (string, boolean, int, etc.).
Entity e = new Entity()
Attribute attr = new Attribute()
attr.setValue(1)
e.addToAttributes(attr)
e.save()
The above code correctly creates all objects, but fails to save the value object. The entity and attribute are saved, but the value is not. Am I missing some identifier needed to cascade all the way down to the value object?
Figured this out. Apparently there is some magic in the grails dynamic setters. I changed the setValue(val) method to set(val) and it started working. Lesson learned: don't override grails' dynamically added methods because they are built with magic, pixy dust, and unicorn urine.