Grails Embedded Classes ClassCastException

by Geoff Lane on April 21st, 2009

Using ORM tools allow you to map the data to a database independently of how your object model looks. Grails supports one-to-many and one-to-one relationships if you want to have the data in different table. But what about when you want to map a single table to multiple objects? In Grails a has a relationship where all the data is stored in a single table is defined by using the embedded syntax. (This creates a component in the Hibernate mapping world.)

An example of using the embedded syntax to create a compositional relationship:

class Person {
    static embedded = ['address']
 
    String name
    int age
    Address address = new Address()
}
 
class Address {
    String street
    String street2
    String city
    String state
    String zip
 
    static constraints = {
        street2(nullable:true)
    }
}

This is all great, but…
When you attempt to databind against the Person model including Address properties, you end up with an exception:

2009-04-17 20:36:10,058 [13260127@qtp2-0] ERROR errors.GrailsExceptionResolver – java.lang.ClassCastException: Address$__clinit__closure1
org.codehaus.groovy.runtime.InvokerInvocationException: java.lang.ClassCastException: Address$__clinit__closure1
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:92)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:234)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1061)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:910)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:892)
at groovy.lang.Closure.call(Closure.java:279)
at groovy.lang.Closure.call(Closure.java:274)

It ends up there is a bug in Grails versions 1.0.4, 1.0.5 and 1.1 that is causing this. That bug is related to the constraints on the embedded class, in this case the Address class. Removing the constraints allows the object to be properly databound, with the obvious downside of lacking constraints.

I have filed a bug about this: GRAILS-4446. So hopefully it will be fixed soon. Hopefully if other people are having this problem they will help them out.

Update:
There is another workaround which is to declare the embedded class in its own file which makes it a first-class Domain object. When that’s done the constraints work as expected. The downside is that this means that a table will be created for the embedded domain class (and never used).

From → Code, Groovy

6 Comments
  1. I saw your archive. You must have altered the views generated by scaffolding – I only get drop downs on the create.gsp when there is any type of relationship between domain classes.

  2. Jeremy,
    Yes, you have to create your own views. The scaffolding doesn’t support embedded classes. In fact the generated view is actually broken if you use composition like this, so you pretty much have to create your own.

  3. stewie permalink

    When creating your own view for Person in your example, what would you call the g:textInputs that for address.street etc? Does it complicate things if Person has a homeAddress and businessAddress?
    Thanks,
    stewie

  4. @stewie,
    In your views, for databinding, you use the name declared as it’s declared in the property. So in your case you would have:

    class Person {
        static embedded = ['homeAddress', 'businessAddress']
        Address homeAddress
        Address businessAddress
    }

    In your view you might have something like:

    <tr class="prop">
        <td valign="top" class="name">
            <label for="homeAddress.street">Street:</label>
        </td>
        <td valign="top" class="value ${hasErrors(bean:personInstance,field:'homeAddress.street','errors')}">
            <input type="text" id="homeAddress.street" name="homeAddress.street" 
                    value="${fieldValue(bean:personInstance,field:'homeAddress.street')}" />
        </td>
    </tr>

    The replace homeAddress with businessAddress for the other values.

  5. Juan permalink

    I’ve tried the workaround declaring the domain class of the embedded object in its own file. The data binding apparently works, but the type conversion errors are omitted. I have a Short property in the embedded object, with a min and a max constraint. If the number is out of range the error is generated and you can ask for it by the hasErrors() method of the main class. But when I try to assign a string to the Short property from the form, and then bind the data, non errors are generated, and the property just get the null value, that in this case it’s allowed in the model constraints.

  6. Juan permalink

    Well, you can ignore the last comment because I was totally wrong. When I tried to assign a String to the Short property, I was doing it by a dijit.form.NumberTextBox, that is a great widget from Dojo javascript framework. I didn’t know that if you type a String on that input widget the String value is never submitted, a blank value is posted instead. Sorry for that comment, now I’m happy to know this is not a bug.

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS