Grails

Domain subclass with embedded property causing MissingPropertyException for "save"

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Critical Critical
  • Resolution: Fixed
  • Affects Version/s: 1.1.1
  • Fix Version/s: 1.1.2, 1.2-M1
  • Component/s: Persistence
  • Description:
    Hide

    quite simply,

    class Party {
    String type

    static constraints = { tablePerHierarchy false }
    }

    class Person extends Party {
    NameInfo name
    static embedded = ['name']
    }

    class Organization extends Party {
    String name
    String taxId
    }

    class NameInfo {
    String firstName
    String lastName
    }

    instantiating an Organization and saving it causes no problems. Instantiating a Person and trying to save it causes the following exception to occur:

    org.codehaus.groovy.runtime.InvokerInvocationException: groovy.lang.MissingPropertyException: No such property: save for class: Person
    at org.jsecurity.web.servlet.JSecurityFilter.doFilterInternal(JSecurityFilter.java:382)
    at org.jsecurity.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:180)
    Caused by: groovy.lang.MissingPropertyException: No such property: save for class: Person
    at TestController$_closure10.doCall(TestController.groovy:215)
    at TestController$_closure10.doCall(TestController.groovy)
    ... 2 more

    Why? Was working previously off a 1.1.1 snapshot. But upgrade to 1.1.1 killed the code.

    Thanks,
    Chris

    Show
    quite simply, class Party { String type static constraints = { tablePerHierarchy false } } class Person extends Party { NameInfo name static embedded = ['name'] } class Organization extends Party { String name String taxId } class NameInfo { String firstName String lastName } instantiating an Organization and saving it causes no problems. Instantiating a Person and trying to save it causes the following exception to occur: org.codehaus.groovy.runtime.InvokerInvocationException: groovy.lang.MissingPropertyException: No such property: save for class: Person at org.jsecurity.web.servlet.JSecurityFilter.doFilterInternal(JSecurityFilter.java:382) at org.jsecurity.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:180) Caused by: groovy.lang.MissingPropertyException: No such property: save for class: Person at TestController$_closure10.doCall(TestController.groovy:215) at TestController$_closure10.doCall(TestController.groovy) ... 2 more Why? Was working previously off a 1.1.1 snapshot. But upgrade to 1.1.1 killed the code. Thanks, Chris
  • Environment:
    OSX JDK 1.6
  1. FaceAndNose-bug-report-22052009.zip
    (16 kB)
    Arnold Franz
    22/May/09 9:38 AM

Activity

Hide
Graeme Rocher added a comment - 21/May/09 11:31 AM

Please attach an example that reproduces the problem.

Show
Graeme Rocher added a comment - 21/May/09 11:31 AM Please attach an example that reproduces the problem.
Hide
Ben Morris added a comment - 22/May/09 7:38 AM

I am getting the same problem. It also includes the delete method. I've got it narrowed down to 2 test cases that have to run one after the other in order to reproduce the problem. I will work on an example, too.

Show
Ben Morris added a comment - 22/May/09 7:38 AM I am getting the same problem. It also includes the delete method. I've got it narrowed down to 2 test cases that have to run one after the other in order to reproduce the problem. I will work on an example, too.
Hide
Arnold Franz added a comment - 22/May/09 9:38 AM

I did this demo application which shows the problem:

class Face {
Integer width
Integer heigth
Nose nose
}
class Nose {
Integer length
static belongsTo = [face:Face]
}

I want to update an existing Nose instance. First, I load the face which owns the nose:
def face = Face.get(faceId)
I get the corresponding nose to update its length :
def nose = face.nose
nose.length = 10

Now, I want to save the nose:
nose.save(flush:true).

In Grails 1.1 : OK, no problem, nose is saved.
In Grails 1.1.1 : KO: I get this error : groovy.lang.MissingPropertyException: No such property: save for class: Nose.
Doing a face.save() works correctly (cascading to nose which is correctly saved (both in Grails 1.1 and 1.1.1)

I seems that Nose is not recognized as a Domain class.

Show
Arnold Franz added a comment - 22/May/09 9:38 AM I did this demo application which shows the problem: class Face { Integer width Integer heigth Nose nose } class Nose { Integer length static belongsTo = [face:Face] } I want to update an existing Nose instance. First, I load the face which owns the nose: def face = Face.get(faceId) I get the corresponding nose to update its length : def nose = face.nose nose.length = 10 Now, I want to save the nose: nose.save(flush:true). In Grails 1.1 : OK, no problem, nose is saved. In Grails 1.1.1 : KO: I get this error : groovy.lang.MissingPropertyException: No such property: save for class: Nose. Doing a face.save() works correctly (cascading to nose which is correctly saved (both in Grails 1.1 and 1.1.1) I seems that Nose is not recognized as a Domain class.
Hide
Chris Brookes added a comment - 25/May/09 1:20 PM

I have this issue too. Only seems to happen when the Child record (Nose in the above example) is retrieved off its owner. It is still possible to create and then save the Child without error, and also retrieve the child directly (i.e. Nose.get(1)) and save it ok, as long as its parent association is not involved.

Show
Chris Brookes added a comment - 25/May/09 1:20 PM I have this issue too. Only seems to happen when the Child record (Nose in the above example) is retrieved off its owner. It is still possible to create and then save the Child without error, and also retrieve the child directly (i.e. Nose.get(1)) and save it ok, as long as its parent association is not involved.
Hide
Chris Chen added a comment - 25/May/09 8:04 PM

As per Burt Beckwith's post, he managed to find a workaround which I haven't tested yet, but I'd like to post it here so that people would know about it.

----snipped from threaded post http://www.nabble.com/save()-method-on-domain-class-seen-as-property-td23659161.html#a23663421 -----

I played with this a bit and it seems like a Groovy bug to me - there doesn't seem to be a sensible Grails-specific reason why it thinks save() is a property invocation and not a method invocation.

I found a workaround though. HibernateGrailsPlugin (using HibernatePluginSupport) wires up the GORM methods and most are lazily initialized to reduce startup time. The save() call is failing to invoke the MissingMethodException that triggers this lazy initialization but I found that if I call DomainClass.count() or list() that it does wire up the methods and save() works after that.

So a hackish fix (until this is fixed in an upcoming release) would be to put count() calls in BootStrap for the problematic domain classes, or even for all of them, e.g.

class BootStrap {
  def grailsApplication
  def init = { servletContext ->
    for (dc in grailsApplication) {
      dc.clazz.count()
    }
  }
}
Show
Chris Chen added a comment - 25/May/09 8:04 PM As per Burt Beckwith's post, he managed to find a workaround which I haven't tested yet, but I'd like to post it here so that people would know about it. ----snipped from threaded post http://www.nabble.com/save()-method-on-domain-class-seen-as-property-td23659161.html#a23663421 ----- I played with this a bit and it seems like a Groovy bug to me - there doesn't seem to be a sensible Grails-specific reason why it thinks save() is a property invocation and not a method invocation. I found a workaround though. HibernateGrailsPlugin (using HibernatePluginSupport) wires up the GORM methods and most are lazily initialized to reduce startup time. The save() call is failing to invoke the MissingMethodException that triggers this lazy initialization but I found that if I call DomainClass.count() or list() that it does wire up the methods and save() works after that. So a hackish fix (until this is fixed in an upcoming release) would be to put count() calls in BootStrap for the problematic domain classes, or even for all of them, e.g.
class BootStrap {
  def grailsApplication
  def init = { servletContext ->
    for (dc in grailsApplication) {
      dc.clazz.count()
    }
  }
}
Hide
dan bachelder added a comment - 30/May/09 11:22 AM

For what it's worth, I first hit this problem when I switched from dbCreate = "create-drop" to dbCreate = "update"... switch back and the problem goes away.

Now I have to have some code similar to what Chris Chen commented above.

Show
dan bachelder added a comment - 30/May/09 11:22 AM For what it's worth, I first hit this problem when I switched from dbCreate = "create-drop" to dbCreate = "update"... switch back and the problem goes away. Now I have to have some code similar to what Chris Chen commented above.
Hide
Jean-Noël Rivasseau added a comment - 05/Jun/09 12:56 PM

I think the severity of this should be bumped to blocker. On about all my classes calling save() does not work anymore since 1.1.1. I can reproduce this all the time, if you need a sample to debug just shout.

Show
Jean-Noël Rivasseau added a comment - 05/Jun/09 12:56 PM I think the severity of this should be bumped to blocker. On about all my classes calling save() does not work anymore since 1.1.1. I can reproduce this all the time, if you need a sample to debug just shout.
Hide
Falk Berger added a comment - 20/Jul/09 9:12 AM

We have the same problem here. Thanks for the hints.

Domainclass.count() in BootStrap.groovy works for us too.

Show
Falk Berger added a comment - 20/Jul/09 9:12 AM We have the same problem here. Thanks for the hints. Domainclass.count() in BootStrap.groovy works for us too.
Hide
Peter Delahunty added a comment - 20/Jul/09 9:30 AM

I also get this problem. However it is only on domain objects from a plugin. Also it is also only on Tomcat 6.0. When I deploy the app for the first time I get the problem. If I then stop and restart the server it goes away. Not good !!

Show
Peter Delahunty added a comment - 20/Jul/09 9:30 AM I also get this problem. However it is only on domain objects from a plugin. Also it is also only on Tomcat 6.0. When I deploy the app for the first time I get the problem. If I then stop and restart the server it goes away. Not good !!
Hide
Dillon Bly added a comment - 07/Aug/09 10:58 AM

In case it is useful to anyone, here is a variation of Chris Chen's workaround that I got to work:

import org.codehaus.groovy.grails.commons.ApplicationHolder

class BootStrap
{
    def init = { servletContext ->
        // workaround for GRAILS-4580
        ApplicationHolder.application.domainClasses.each { dc ->
            dc.clazz.count()
        }
    }
}
Show
Dillon Bly added a comment - 07/Aug/09 10:58 AM In case it is useful to anyone, here is a variation of Chris Chen's workaround that I got to work:
import org.codehaus.groovy.grails.commons.ApplicationHolder

class BootStrap
{
    def init = { servletContext ->
        // workaround for GRAILS-4580
        ApplicationHolder.application.domainClasses.each { dc ->
            dc.clazz.count()
        }
    }
}
Hide
Peter De Winter added a comment - 31/Aug/09 5:35 AM

The work around throws a new type of exception.

I have the same problem as described above, but within the bootstrap. I' m trying to fill up the database with a little default data and when i switched from dbCreate: "create-drop" to "update" the trouble started.

So what I do create a few parents and try to save them and the exception appears. If I read well everyone here got the exception on child instances.

After applying the described workarounds I now get the following error:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Parent

Show
Peter De Winter added a comment - 31/Aug/09 5:35 AM The work around throws a new type of exception. I have the same problem as described above, but within the bootstrap. I' m trying to fill up the database with a little default data and when i switched from dbCreate: "create-drop" to "update" the trouble started. So what I do create a few parents and try to save them and the exception appears. If I read well everyone here got the exception on child instances. After applying the described workarounds I now get the following error: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Parent
Hide
Guillaume Jeudy added a comment - 12/Nov/09 9:19 AM

I can't tell exactly when I started getting this exception, I was working through the examples in Definitive guide to grails 2nd edition and up to Chapter 16 (adding JMS) I started getting the exception.

I tried a bunch of measures (restart app, clean, call domainclass.count() in bootstrap) and the only measure that worked was to switch back the database from "update" to "create-drop" and use Dillon's workaround in Bootstrap.groovy.

This issue is a little scary, let's hope the patch due in 1.1.2 definitively fixes it.

Show
Guillaume Jeudy added a comment - 12/Nov/09 9:19 AM I can't tell exactly when I started getting this exception, I was working through the examples in Definitive guide to grails 2nd edition and up to Chapter 16 (adding JMS) I started getting the exception. I tried a bunch of measures (restart app, clean, call domainclass.count() in bootstrap) and the only measure that worked was to switch back the database from "update" to "create-drop" and use Dillon's workaround in Bootstrap.groovy. This issue is a little scary, let's hope the patch due in 1.1.2 definitively fixes it.

People

Dates

  • Created:
    19/May/09 3:58 AM
    Updated:
    12/Nov/09 9:19 AM
    Resolved:
    09/Jun/09 8:38 AM