Search code examples
hibernatetestinggrailsgrails-orm

GORM Mapping Manifesto :: To Long or not to Long


Fairly basic problem but it's roots run deep in the framework (and there is little definitive information on the subject), so I am putting it out here so as to save others the pain (and to verify that I am correct or not in my thinking).

What is the problem?

Grails automatically injects an id field of type Long into your domains (see Beckwith's comment). When using a legacy DB Grails maps Longs to bigints by default, an inefficient storage type that anyone dealing with large million+ record tables would avoid.

Upon discovering this a few months ago I set to work on getting a "proper" column type in place for my domain ids. Not having a Java background, blindly I thought, Long-bad, Integer-good and set hibernate mapping dialect for Integer types to what I would do by hand in mysql

registerColumnType(Types.INTEGER, 'mediumint unsigned')

and then defined "Integer id" in all of my domains (not necessary according to Bert's comment in link above). Everything worked swimmingly, wonderful, on to other things.

Fast forward to Grails 2.0 (as I could not resist all of the goodies ;-)) and Spock. For the life of me I could not figure out why, despite 2.0 new in-memory GORM and support for dynamic finders, that Domain.findByFoo(fooVal) would always return null (yes, I @Mock(Domain) and populate with test data). In fact, both within the test itself and @TestFor target, the only GORM methods that worked were save() and get(); everything else returned null.

I whipped up a quick test app, domain + controller + spoc spec and discovered the source of the problem: If you use a property type other than Long for your ids (including referenced FKs), your @Mock domain(s) will be rendered useless.

So, am I correct or not in saying that one must use Long ids in order to take full advantage of the framework? @Mock + @TestFor + Spock is an incredible combo! Guidance appreciated before I head down refactor-to-Long road...


Solution

  • I cannot imagine any realistic scenario where the difference between Integer and Long would make any noticeable difference to performance (which seems to have been the original motivation for making this change).

    If using Long works, but Integer causes problems, then use Long and move onto more important tasks. I would only worry about this if you can prove that using Integer makes any significant difference.

    Update

    You're right, I did completely miss the point about the database type that Grails automatically uses for Long. As you seem to already know, you can control the database type in the mapping closure of your domain class, e.g.

    class Person {
       Long id
    
       static mapping = {
          // See here for alternatives to integer, and how they map to DB types
          // http://docs.jboss.org/hibernate/stable/core/manual/en-US/html/mapping.html#mapping-types-basictypes
          id type:'integer'
       }
    }
    

    You also brought up in the comments

    dealing with Long at code level where one must specify def foo(Long id) {...} and params.id.toLong() as opposed to Integer

    You can simply define an action as

    def myAction = {Person p ->
    
    }
    

    or

    def myAction = {
       Person p = new Person(params)
    }
    

    And Grails will take care of type-converting the id request parameter to Person.id, regardless of whether it's a Long, Integer, BigDecimal, etc.