Question

Is it possible to explicitly set the id of a domain object in Grails' Bootstrap.groovy (or anywhere, for that matter)?

I've tried the following:

new Foo(id: 1234, name: "My Foo").save()

and:

def foo = new Foo()
foo.id = 1234
foo.name = "My Foo"
foo.save()

But in both cases, when I print out the results of Foo.list() at runtime, I see that my object has been given an id of 1, or whatever the next id in the sequence is.

Edit: This is in Grails 1.0.3, and when I'm running my application in 'dev' with the built-in HSQL database.

Edit: chanwit has provided one good solution below. However, I was actually looking for a way to set the id without changing my domain's id generation method. This is primarily for testing: I'd like to be able to set certain things to known id values either in my test bootstrap or setUp(), but still be able to use auto_increment or a sequence in production.

Was it helpful?

Solution

Yes, with manually GORM mapping:

class Foo {
  String name
  static mapping = {
    id generator:'assigned'
  }
}

and your second snippet (not the first one) will do the job (Id won't be assigned when passing it through constructor).

OTHER TIPS

What I ended up using as a workaround was to not try and retrieve objects by their id. So for the example given in the question, I changed my domain object:

class Foo {
  short code /* new field */
  String name

  static constraints = {
    code(unique: true)
    name()
  }
}

I then used an enum to hold all of the possible values for code (which are static), and would retrieve Foo objects by doing a Foo.findByCode() with the appropriate enum value (instead of using Foo.get() with the id like I wanted to do previously).

It's not the most elegant solution, but it worked for me.

As an alternative, assuming that you're importing data or migrating data from an existing app, for test purposes you could use local maps within the Bootstrap file. Think of it like an import.sql with benefits ;-)

Using this approach:

  1. you wouldn't need to change your domain constraints just for testing,
  2. you'll have a tested migration path from existing data, and
  3. you'll have a good data slice (or full slice) for future integration tests

Cheers!

def init = { servletContext ->

    addFoos()
    addBars()

}

def foosByImportId = [:]
private addFoos(){
    def pattern = ~/.*\{FooID=(.*), FooCode=(.*), FooName=(.*)}/
    new File("import/Foos.txt").eachLine {
        def matcher = pattern.matcher(it)
        if (!matcher.matches()){
            return;
        }

        String fooId = StringUtils.trimToNull(matcher.group(1))
        String fooCode = StringUtils.trimToNull(matcher.group(2))
        String fooName = StringUtils.trimToNull(matcher.group(3))

        def foo = Foo.findByFooName(fooName) ?: new Foo(fooCode:fooCode,fooName:fooName).save(faileOnError:true)
        foosByImportId.putAt(Long.valueOf(fooId), foo) // ids could differ
    }
}

private addBars(){
    ...
    String fooId = StringUtils.trimToNull(matcher.group(5))
    def foo = foosByImportId[Long.valueOf(fooId)]
    ...
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top