Grails many-to-many & Join table

What I enjoy with frameworks like Grails or Rails is that they are taking care of all the object relational mapping for me. So, I can really concentrate on the domain object model and the framework will care about the underlying work. Fortunately, when needed, I can still “talk” to the database directly. Also, with the scaffolding I can quickly get a feeling for the application and see if the domain model makes any sense.

The many-to-many relationship and how Grails treats it is kind of interesting.

Let’s take an example. In a project, I have two domain classes that are connected with a many-to-many relationship, ExpDesign, ExpConditionValue. ExpDesign represents an experimental design, ExpConditionValue represents a value for a condition in a design. So an experimental design (ExpDesign) can have multiple condition values (ExpConditionValue) and those can belong to many ExpDesign. So, that is clearly a many-to-many relationship which needs a join table and Grails knows it.
This is how we tell Grails (with hasMany) about the many-to-many relationship:

class ExpDesign {
    static hasMany = [expConditionValues: ExpConditionValue]
    ...
}
class ExpConditionValue {
    static hasMany = [expDesigns: ExpDesign]
    ...
}

But actually, that’s not enough. We also need to tell Grails (with belongsTo) which class is the owner of the other (more about belongsTo later) :

class ExpConditionValue {
    static hasMany = [expDesigns: ExpDesign]
    static belongsTo = [ExpConditionType]
}

It’s clear that ExpDesign owns ExpConditionValue, therefore I put the belongsTo in ExpConditionValue.

Before I run the code, I want to modify it to specify the names of the mapping tables for my two classes. My convention is to add underscores before upper case letters and pluralize the domain name to define the table name. So here, the tables would be called EXP_DESIGNS and EXP_CONDITION_VALUES. To define the join table name, Grails appends both names with an underscore, so here it would be EXP_DESIGNS_EXP_CONDITION_VALUES. However, as I use Oracle, it will not work because of the length of the table name. I figured this out running describe all_tab_columns (did you know that Oracle limits table name length to 30 characters?). So, I want to specify not only the tables names but also the join table name.

The modified code:

class ExpDesign {
    static mapping = {
        table 'EXP_DESIGNS'
        expConditionValues joinTable: 'EXP_COND_VALS_DESIGNS'
    }

    static hasMany = [expConditionValues: ExpConditionValue]
    ...
}
class ExpConditionValue {
    static mapping = {
        table ' EXP_COND_VALUES'
        expDesigns joinTable: 'EXP_COND_VALS_DESIGNS'
    }
    static hasMany = [expDesigns: ExpDesign]
    static belongsTo = [ExpDesign]
    ...
}

With that I let Grails take care about the relationship, I just enforced the names of the domain tables and the join table.

belongsTo

belongsTo tells grails that the other class is the owning class in the relationship. In my case, ExpDesign owns (many) ExpConditionValue. The first consequence is that ExpDesign deletes will be cascaded to ExpConditionValues. Whenever a ExpDesign will be deleted all its associated ExpConditionValues will be deleted as well, without the belongsTo they would not.
In the case above, I have used the unidirectional belongsTo clause but a bidirectional version exists as well (e.g. static belongsTo = [expD : ExpDesign]) which would enable to navigate from the object to its owning object.

What is interesting as well is that belongsTo combined to hasMany tell Grails enough about our model that it will be able to add up some dynamic methods like exp.addToExpConditionValues(val1) that will automatically persist objects to the DB.

Thanks to grails, a lot of the magic happens in the background and for us much less tedious code to write!

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>