Townsfolk
Townsfolk

Reputation: 1334

Gorm one-to-one and one-to-many

I have two objects that have a one-to-one and one-to-many relationship.

class GroupMember {
   /**
    * InvitationCode used to join this Group
    */
   InvitationCode invitationCode

   static constraints = {
      // group creator member object won't have used an invitation code
      invitationCode(nullable: true) 
   }
}

class InvitationCode {
   /**
    * GroupMember who created the code, not used the code
    */
   GroupMember creator

   // I tried removing this, it has no effect.
   static hasMany = [members: GroupMember]
}

Basically, I have a GroupMember who owns an InvitationCode and can use another InvitationCode. Or, an InvitationCode can only belong-to/be-created-by one GroupMember, but can be used/referenced by many GroupMember's

The tables look like they're set up correctly - Both tables have fields for the other: INVITATION_CODE.CREATOR_ID and GROUP_MEMBER.INVITATION_CODE_ID.

However, when I create a new InvitationCode it seems the GroupMember object is being updated and the invitationCodeId is being set to the newly created InvitationCode's id.

GroupMember creator = new GroupMember(
   invitationCode: null // group creator - didn't use an invitation code
)
creator.save(flush: true)

InvitationCode invitationCode = new InvitationCode(
   creator: creator
)
invitationCode.save(flush: true)

GroupMember _creatorMember = GroupMember.findById(creator.id)
assert _creatorMember.invitationCode == invitationCode // true??

I never set the creator.invitationCode so I'm not sure how/why GORM is setting it. I'm also not sure where my mistake is in defining the domain. When I remove the flush: true's I get a foreign-key restraint violation on GroupMember.invitationCode.

Upvotes: 0

Views: 290

Answers (1)

tylerwal
tylerwal

Reputation: 1880

The problem with your current setup is that the InvitationCode domain has 2 references to the GroupMember domain and grails has incorrectly inferred (in your case) that:

  • invitationCode and creator have a bidirectional association with each other
  • members is a unidirectional association to the GroupMember class

But in reality, you want:

  • creator to be a unidirectional association to the GroupMember class
  • members to be a many-to-one relationship to the back-reference invitationCode, making it a bidirectional association

Understandably, grails has a problem 'guessing' how to form this complex relationship.

The key is going to be the use of the mappedBy property. I've used it successfully in mapping 2 many-to-many relationships on the same 2 classes, but not one like you need. I think what you need would be to have this in your InvitationCode class:

static mappedBy = [
    members: 'invitationCode',
    creator: 'none' // because the relationship is unidirectional
]

Hopefully this works for you!

Upvotes: 1

Related Questions