Grails
  1. Grails
  2. GRAILS-4031

Mapping Definition Not Inherited by Subclasses w/Mapping Definition

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Blocker Blocker
    • Resolution: Fixed
    • Affects Version/s: 1.1-beta3
    • Fix Version/s: 2.0-M1
    • Component/s: Persistence
    • Labels:
      None

      Description

      Using 1.1b3 I am unable to change a field's mapping type in the db for a subclass if that subclass has a mapping definition itself. In this case the default in the DB for a Boolean would be 0/1 and I'm trying to use Y/N. If there's no mapping definition in the subclass the field is changed to Y/N. This is sort of related to http://jira.codehaus.org/browse/GRAILS-3130, but wanted to document that this case seems a bit worse in that I found no obvious workaround.

      abstract class Parent {
        Boolean active
        static mapping = {
          active type: 'yes_no'
        }
      }
      
      //WITH NO MAPPING WE'RE OK
      class Child extends Parent {
        String someField
      }
      
      //DOESN'T WORK...
      class Child extends Parent {
        String someField
      
        static mapping = {
          sort: someField
        }
      }
      
      //DOESN'T WORK...
      class Child extends Parent {
        String someField
      
        static mapping = {
          active type: 'yes_no'
          sort: someField
        }
      }
      
      //DOESN'T WORK...
      class Child extends Parent {
        Boolean active //DUPLICATED FROM PARENT
        String someField
        
        static mapping = {
          active type: 'yes_no'
          sort: someField
        }
      }
      

        Activity

        Hide
        Graeme Rocher added a comment -

        If you change your parent class to non-abstract it works as expected

        Show
        Graeme Rocher added a comment - If you change your parent class to non-abstract it works as expected
        Hide
        Tyler Williams added a comment -

        I guess I would have to disagree on the definition of 'expected'. If I remove the abstract keyword, then all of the subclasses end up in a single DB table. In my current project, I have a base class that the majority of DOs extend. The "Boolean active" field is in the base class so I don't want to have a single table for 90% of my DOs.

        In the broader perspective I personally try to end up with a database model that makes sense independent of Grails or any specific framework. This is simply a best practice from my experience because the database often outlives a specific application or is leveraged by multiple applications, especially in larger orgs. Additionally these orgs have very specific conventions around their datamodeling requirements.

        On the past couple of non-trivial Grails projects, I have run into a similar type of problem where I can get about 98% of the way there using GORM but eventually resort to redoing the whole model using Hibernate directly due to one of the many inconsistencies with Grails handling of field and mapping definitions in subclasses. Granted things have gotten better but there are still too many edge cases left to get tripped up by. It just seems like a shame to have so much available in GORM but still not be able to use it due to "obvious" and somewhat minor gaps in behavior.

        These "broken" cases seem like they fall into one of two categories. I think it would be reasonable to expect the first set be supported ASAP?

        • Standard: Single level abstract subclassing. Consistent inheritance of field and mapping definitions. Overriding of field and mapping definitions.
        • Obscure: Multi-level inheritance. Self-referencing associations.

        Here's the bottom line for me:
        – Any complex enterprise app will end up with one or more subclassed Domain class scenarios
        – Some low level tweaking of subclass properties or mappings will be required
        – GORM does not currently handle these situations effectively

        So the usage path for Grails under the above project conditions could be one of the following:
        1) Use GORM with the understanding that subclassing may be problematic and the timeframe for a GORM-based solution is unknown (it appears to have been pushed back several times)
        2) Use Hibernate from the start and exclusively (maybe this is really the best practice?)
        3) Issues with GORM subclassing behavior are escalated and addressed in the next release (post 1.1 would possibly be another 4+ months down the road I'd imagine..probably too long/uncertain for any project in development or even in planning)

        Show
        Tyler Williams added a comment - I guess I would have to disagree on the definition of 'expected'. If I remove the abstract keyword, then all of the subclasses end up in a single DB table. In my current project, I have a base class that the majority of DOs extend. The "Boolean active" field is in the base class so I don't want to have a single table for 90% of my DOs. In the broader perspective I personally try to end up with a database model that makes sense independent of Grails or any specific framework. This is simply a best practice from my experience because the database often outlives a specific application or is leveraged by multiple applications, especially in larger orgs. Additionally these orgs have very specific conventions around their datamodeling requirements. On the past couple of non-trivial Grails projects, I have run into a similar type of problem where I can get about 98% of the way there using GORM but eventually resort to redoing the whole model using Hibernate directly due to one of the many inconsistencies with Grails handling of field and mapping definitions in subclasses. Granted things have gotten better but there are still too many edge cases left to get tripped up by. It just seems like a shame to have so much available in GORM but still not be able to use it due to "obvious" and somewhat minor gaps in behavior. These "broken" cases seem like they fall into one of two categories. I think it would be reasonable to expect the first set be supported ASAP? Standard: Single level abstract subclassing. Consistent inheritance of field and mapping definitions. Overriding of field and mapping definitions. Obscure: Multi-level inheritance. Self-referencing associations. Here's the bottom line for me: – Any complex enterprise app will end up with one or more subclassed Domain class scenarios – Some low level tweaking of subclass properties or mappings will be required – GORM does not currently handle these situations effectively So the usage path for Grails under the above project conditions could be one of the following: 1) Use GORM with the understanding that subclassing may be problematic and the timeframe for a GORM-based solution is unknown (it appears to have been pushed back several times) 2) Use Hibernate from the start and exclusively (maybe this is really the best practice?) 3) Issues with GORM subclassing behavior are escalated and addressed in the next release (post 1.1 would possibly be another 4+ months down the road I'd imagine..probably too long/uncertain for any project in development or even in planning)
        Hide
        Graeme Rocher added a comment -

        If you want multiple tables use:

        static mapping = {
            tablePerSubclass true
        }
        

        However as I said the definition of the bug is incorrect, mappings are inherited from parent to subclasses. The issue is that currently Grails does not support root classes that are abstract. It treats relationships like this as if there was no inheritance involved at all.

        Show
        Graeme Rocher added a comment - If you want multiple tables use: static mapping = { tablePerSubclass true } However as I said the definition of the bug is incorrect, mappings are inherited from parent to subclasses. The issue is that currently Grails does not support root classes that are abstract. It treats relationships like this as if there was no inheritance involved at all.
        Hide
        Roel van Dijk added a comment -

        "The issue is that currently Grails does not support root classes that are abstract."

        Any idea when this will be supported? This is a very common situation, to have an abstract class as the root of a class hierarchy (I think).

        Show
        Roel van Dijk added a comment - "The issue is that currently Grails does not support root classes that are abstract." Any idea when this will be supported? This is a very common situation, to have an abstract class as the root of a class hierarchy (I think).
        Hide
        Goran Ehrsson added a comment -

        I was surprised to find that it's not possible to have mappings defined in an abstract class and override it in subclasses.

        I tried to add

        static mapping = {
          id generator: 'assigned'
        }
        

        in an abstract root class for all my entities. But the 'assigned' generator was not applied to the subclasses.
        The id column was created as

        id varchar(32) generated by default as identity (start with 1)
        
        Show
        Goran Ehrsson added a comment - I was surprised to find that it's not possible to have mappings defined in an abstract class and override it in subclasses. I tried to add static mapping = { id generator: 'assigned' } in an abstract root class for all my entities. But the 'assigned' generator was not applied to the subclasses. The id column was created as id varchar(32) generated by default as identity (start with 1)
        Hide
        Goran Ehrsson added a comment -

        I have this workaround in my subclasses:

        class SubDomainClass {
          ...
          static mapping = {
            def pm = BaseDomainClass.mapping.clone()
            pm.delegate = delegate
            pm.call()
            sort 'orderIndex'
            cache usage:'nonstrict-read-write'
          }
        }
        
        Show
        Goran Ehrsson added a comment - I have this workaround in my subclasses: class SubDomainClass { ... static mapping = { def pm = BaseDomainClass.mapping.clone() pm.delegate = delegate pm.call() sort 'orderIndex' cache usage:'nonstrict-read-write' } }
        Hide
        Graeme Rocher added a comment -

        This is already fix in 2.0 but added a test to verify it

        Show
        Graeme Rocher added a comment - This is already fix in 2.0 but added a test to verify it
        Hide
        Patraphong Sukhonpitumart added a comment -

        Is it by design that a domain class does not inherit mapping from its abstract parent class in the src/groovy folder? Specifically, I add type: 'text' to an abstract parent class but the resulting database field of the child class is of VARCHAR(255) instead of TEXT.

        I'm using Grails 2.0.0-M1 on Microsoft SQL Server 2008.

        Show
        Patraphong Sukhonpitumart added a comment - Is it by design that a domain class does not inherit mapping from its abstract parent class in the src/groovy folder? Specifically, I add type: 'text' to an abstract parent class but the resulting database field of the child class is of VARCHAR(255) instead of TEXT. I'm using Grails 2.0.0-M1 on Microsoft SQL Server 2008.
        Hide
        Graeme Rocher added a comment -

        Yes it is by design, the abstract parent class in src/groovy is not treated as a persistent class unless it is in grails-app/domain

        Show
        Graeme Rocher added a comment - Yes it is by design, the abstract parent class in src/groovy is not treated as a persistent class unless it is in grails-app/domain

          People

          • Assignee:
            Graeme Rocher
            Reporter:
            Tyler Williams
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development