Trying not to be gormless with GORM

Revision 1:

I intend to re-edit this post, so it turns into something more informative than a question as I experiment with creating the Domain model for the first phase of my Grails portal.
There are a couple of things I’m struggling with at the moment:

  1. The first is to do with Many to Many relationships:
    Lets’ say I want to model the association between ‘Person’ and the ‘LinkedIn Group’ they are associated to…
    My background is more of a data modelling perspective than a object orientated one, so by instinct I’d be creating the association table ‘LinkedInGroup member’.
    If I delete the ‘LinkedInGroup’ I want to delete all the associated ‘LinkedInGroup member’ records.
    By the same token if I want to delete a ‘Person’, I’d delete all their ‘LinkedInGroup member’ records.
    If I understand the way Grails GORM works correctly, you need to specify belongsTo to ensure cascading database operations occur.
    a) Can I have two belongsTo relations, so cascading deletes work the way I want? I’m not sure if you can have two! Would appreciate clarification on this.
    b) Do I create an association domain class in my model? My gut instinct is I only need to do this if I want to add properties at the association level. So no, I don’t want to go that route, as I can see no intrinsic value adding this domain class.
    c) So my dilemma is how do I best model such a relationship for this scenario?
  2. I want to model an association between Email and Person. There can be many emails per person, but I want to tag one as the ‘primary’ So I have a 1-M relationship. How do I model such a scenario and how best do I override primary in the app. If one email is already tagged as primary, I automatically want to let a new email to become the new primary and switch the primary flag on the existing record to false, as there can be only one primary per person.

Comments are most welcome.

Have been reading:

Revision 2:

I’m just getting more and more lost and disheartened in this quagmire that is GORM….
Here’s what my findings are to date. This is based off Stephane Maldini’s orgininal suggestion:

Person.groovy

LinkedInGroup.groovy

LinkedInGroupMember.groovy

This is the list of tables that get generated from this!:

Tables Grails creates from GORM

Here is the schema for each:

Person schema

LinkedInGroup schema

LinkedInGroupMember schema

LinkedInGroup LinkedInGroupMember Schema

Person LinkedInGroupMember schema

Five tables!. I’m cringing a bit at this. But I figure what the heck, let’s have a play with this in the Grails Console:

Grails Console not recognising AddTo dynamic properties

Unresolved issue:

Now from what I’ve read in Grails in Action, the belongsTo relationship should facilitate dynamic properties being added to the GORM classes. Why is the Grails Console not acting like the integration test environment? Can you invoke it in this mode? It’s either this or me plonking this in the bootstrap code….

It’s possibile I’ve got the answer for this. ‘grails interactive‘. I commented upon this here (citing Grails podcast 58 & Grails 1.03 release notes) and on the Grails mailing list.

Revision 3:

I was just reading Dave Klein’s Grails Quick Start Guide again.. P53 and I’m also thinking the LinkedInGroup member should look more like this:

LinkedInGroup Member.groovy (v2)

WTF! Now this now seems to work for me! I don’t know what I did wrong before, but I was getting:

java.lang.ClassCastException: java.lang.Class cannot be cast to java.util.Map

and the app wouldn’t boot when I tried this earlier… Back to sensible number of tables in schema now again too… The Grails.org website sucks on this point. :-( You need the attribute as well as the belongsTo in your class…

Finally getting somewhere. :-) Here is the revised Grails Console code. Still had problems with AddTo* properties, but found a way to work around things.

Had to save the LinkedInGroup & Person records before assigning Memberships.

Will have a go at refactoring some of this stuff so it’s easier for a client to call…

Grails Console test v2

tables in database v2

Records in LinkedInGroupMember

Will now see how cascade deletes behave & what type of client side code is required to avoid exceptions.

Revision 4:

Hmm. as expected..

If I add p1.delete() to the end I get:
Exception thrown: deleted object would be re-saved by cascade (remove deleted object from associations): [jgf.LinkedInGroupMember#1]
So, do I need to manually handle this stuff? If so, what’s the point of having belongsTo!

Revision 5:

Reading on again..

DGG 2nd ed P254 , makes ref to static mapping with cascade option. Gives this link as guidance.

Groovy & Grails Recipes by Bashar Abdul-Jawad goes one better and describes this in quite some detail on P265-266.

Just found it in the ref guide too.

Will give this a try on Person & LinkedInGroup:

static mapping = {
  memberships cascade:"delete"
}

Revision 6:

Person.groovy with cascade

LinkedInGroup.groovy with cascade

Worked like a treat:

Grails Console v3 with deletes

Person after delete

LinkedInGroup after delete

LinkedInGroupMember after delete

Revision 7:

The next tweak is to remove the version column from the association table LinkedInGroupMember.

Stephane Maldini mentioned this in the mailing list and you can find it here too (Stephane you don’t use a colon)
Whilst I’m  on the topic of GORM, this link is useful too for automatic time-stamping.

Here is the upgraded version of that class:

LinkedInGroupMember.groovy less version

LinkedInGroupMember less version schema

I think you could also do stuff with a join table to remove the superfluous id column from LinkedInGroupMember.

But I think that leads to more complicated GORM code and I prefer to keep things simple for now. I’m not constrained by a DBA at the moment!

Will have  to ensure the same Person/LinkedInGroup don’t get added twice.

I’ll do some more reading, as I think there ought to be some kind of constraint to protect against this and add a new revision.

Revision 8:

Killed two birds with one stone. Used composite id.

LinkedInGroupMember.groovy v3 composite key - drops id column

LinkedInGroupMember schema shown without id column

Data populated again in LinkedinGroup Members (composite key before deletes)

Sources of information this time around:
Grails.org – GORM – Mapping DSL (forgets to mention class must implement Serializable as the code above shows).

DGG 2nd ed. P531. Listing 17-18 fills in the missing piece!

Revision 9:

Added index to do the inverse sort of the association table by Person_id, so the deletes occur more efficiently. Not without some more hiccups..
See this post

LinkedInGroupMember.groovy (with index)

Revision 10:

You don’t need the LinkedInGroup & Person as Properties on class. Grails pulls them in as long as the belongsTo uses a Map syntax instead of a List ie [col:Class] instead of [Class]

LinkedInGroupMember

I found this out when I was applying a similar process to a class called Competency in my Domain Model. See this blog post.

You don’t have an insane number of tables in the database everything stays the same.

Revision 11:

Added grails interactive comment back on revision 2 regarding ‘unresolved issue’, possibly closed…

Advertisements

About this entry