1

I have the following "top-level" (parent) domain entities:

Customer
Company
Contact

And the following child entity:

Address

There is one to many relationship between each of the top level domain entities:

Customer -> Address
Company -> Address
Contact -> Address

i.e. a customer, company or contact can have one or more addresses.

Unfortunately I do not know how to model this in grails / gorm. It seems I can only define one parent or belongsTo declaration in address i.e. I was not able to declare Address using:

Address {
    Customer parent //??
    Company parent //??
    Contact parent //??
}

Can someone tell me if I am missing something or if it's possible to define this type of relationship in a supported way?

Thanks,

cowper

Conor Power
  • 686
  • 1
  • 6
  • 17
  • Maybe related: http://stackoverflow.com/questions/5533305/grails-belongsto-cascade-on-delete-when-belongsto-specifies-multiple-classes – tim_yates Oct 21 '11 at 22:08

2 Answers2

3

You should be able to use the array version of belongsTo as Tim pointed out:

Address {
    static belongsTo = [Customer, Company, Contact]
}

If entities can share a common address may change the way you configure deletes.

Another option is to have those three classes inherit the property from a superclass, but whether or not that makes sense in your case, I don't know (it kind of doesn't look like it).

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
  • 1
    I generally prefer the map approach static belongsTo = [customer:Customer, company:Company, contact:Contact] – Gregg Oct 22 '11 at 15:37
  • thanks for that. I was having an issue with illegal backRef association when I tried it that way, but searching for that issue indicates another problem that I need to resolve and this should work. ta' – Conor Power Oct 24 '11 at 21:17
  • @cowper Yeah, sometimes other issues confuse things :/ More of a PITA in highly-dynamic, "magic" environments. Good luck! – Dave Newton Oct 24 '11 at 21:26
1

In our application we have several entities that need addresses. But we've chosen to model them in a many-to-many relationship.

Address looks like this

class Address {

  // typical address properties

  Set<Company> getCompanies() {
    CompanyAddress.findAllByAddress(this).collect { it.company } as Set
  }

  static constraints = {
    // typical constraints
  }
}

And for each "parent" we provide a getter. You can see getCompanies() in the code above. If you're only every going to have 1 company per address, then simply have that getter return the 1 company instead of a Set. The inverse is true inside Company, we have a getAddresses().

Company Address, for example, looks like this...

class CompanyAddress implements Serializable{

  Address address
  Company company

  boolean equals(other) {

    if (this.is(other)){
      return true
    }

    if (!(other instanceof CompanyAddress)) {
      return false
    }

    other.address?.id == address?.id &&
        other.company?.id == company?.id
  }

  int hashCode() {
    def builder = new HashCodeBuilder()
    if (address) builder.append(address.id)
    if (company) builder.append(company.id)
    builder.toHashCode()
  }

  static CompanyAddress get(long companyId, long addressId) {
    find 'from CompanyAddress where address.id=:addressId and company.id=:companyId',
      [addressId: addressId, companyId: companyId]
  }

  static CompanyAddress create(Company company, Address address, boolean flush = false) {
    new CompanyAddress(address: address, company: company).save(flush: flush, insert: true)
  }

  static boolean remove(Company company, Address address, boolean flush = false) {
    CompanyAddress instance = CompanyAddress.findByAddressAndCompany(address, company)
    instance ? instance.delete(flush: flush) : false
  }

  static void removeAll(Address address) {
    executeUpdate 'DELETE FROM CompanyAddress WHERE address=:address', [address: address]
  }

  static void removeAll(Company company) {
    executeUpdate 'DELETE FROM CompanyAddress WHERE company=:company', [company: company]
  }

  static mapping = {
    id composite: ['address', 'company']
    version false
  }
}
Gregg
  • 34,973
  • 19
  • 109
  • 214
  • thanks Gregg, I like the solution and may use it in the future. I assume modeling it in this way loses the ability to use criteria queries across the relationship? any other downsides you've come across? – Conor Power Oct 24 '11 at 21:31
  • No I don't. And it solves a lot of performance issues (at least until Grails 2.0 bags implementation). http://www.infoq.com/presentations/GORM-Performance – Gregg Oct 24 '11 at 22:56