Grails Embedded Classes ClassCastException

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).

About Geoff Lane

I’m Geoff Lane and I write Zorched.net as I figure things out about software development in the hopes that it can help other people facing similar situations. Also as a thanks to the larger web community for all of the information and knowledge that they have shared. I’ve been a professional software developer since 1999 working with a variety of different technologies. I’ve worked for startups in the Silicon Valley and Chicago, IL and now work as a consultant building custom applications for clients.
This entry was posted in Code, Groovy and tagged , , . Bookmark the permalink.

6 Responses to Grails Embedded Classes ClassCastException

  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. Geoff Lane says:

    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 says:

    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. Geoff Lane says:

    @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 says:

    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 says:

    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

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang="" line="" escaped=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>