Search code examples
hibernategrailsgrails-orm

Grails - Duplicate property mapping of IndexBackref


I'm facing a weird issue in grails (3.1.11) closely related to this blog post (from 2010 !): http://codedumpblog.blogspot.de/2010/02/grails-many-to-many-with-lists.html

I'm trying to model the following simple relations:

  • There are two parent types (Organization and Person) that share a child type (Address) in a hasMany association.
  • An Address can only belong to one of the parents, never to both simultaneously.
  • Deleting either an Organization or a Person should delete all its Address-es.

So far I have the following code:

class Organization {
    List addresses

    static hasMany = [addresses: Address]

    static mapping = {
        addresses sort: 'country', order: 'asc', cascade: "all-delete-orphan"
    }
}

class Person {
    List addresses

    static hasMany = [addresses: Address]

    static mapping = {
        addresses sort: 'country', order: 'asc', cascade: "all-delete-orphan"
    }
}

class Address {
    String country
    //...truncated...

    static belongsTo = [organization: Organization,
                        person  : Person]

    static constraints = {
        organization nullable: true
        person nullable: true
    }
}

But after running this I get the following Hibernate exception:

org.hibernate.MappingException: Duplicate property mapping of _addressesIndexBackref found in com.example.Address

As in the blog post, the problem only shows up if the addresses field has the same name in both parent classes. If I rename the fields to organizationAddresses and personAddresses respectively, then everything works as expected.

I'd like the field to remain as just addresses though, so I don't have to call stuff like organization.organizationAddresses and person.personAddresses.

Is there a modern workaround for this almost 7 year old issue?


Solution

  • The Problem


    This looks like a Hibernate Bug. Occurs when you create a Class with two relationships connected by many-to-one

    In your case, there are two classes related to Address by hasMany


    The Solution


    Replace the membership relationship by use relationship. In your case, this is done like this:

    Create a class ListAddresses to hold the adresses of a Person or Organization:

    class ListAddresses {
        List addresses
    
        static hasMany = [addresses: Address]
    
        static mapping = {
            addresses sort: 'country', order: 'asc', cascade: "all-delete-orphan"
        }
    }
    

    Delete all relationships from Address and create the new one:

    class Address {
        String country
        //...truncated...
    
        static belongsTo = [list:ListAddresses]
        /*static belongsTo = [organization: Organization,
                        person  : Person]*/
    
        static constraints = {
            /*organization nullable: true
            person nullable: true*/
        }
    }
    

    Use ListAddresses in Person or Organization

    class Organization {
        ListAddresses addresses
    
        static mapping = {
            addresses cascade: "all-delete-orphan"
        }
    }
    
    class Person {
        ListAddresses addresses
    
        static mapping = {
            addresses cascade: "all-delete-orphan"
        }
    }
    

    This answer is based on the question. But in my case, the solution was easier because I replace the belongsTo relationship and not hasMany.