using domain classes in migration / using insert migration

23 views
Skip to first unread message

stefan

unread,
Feb 23, 2009, 9:21:21 AM2/23/09
to Autobase
Hi,

Just gave the Autobase plugin a first try - and ended up with 2
questions / issues so far.

1) I'd like to set up the initial data using a migration instead of
coding in BootStrap.groovy.

Following http://tinyurl.com/chmc6n, I created a groovy migration and
put some code using a domain class in it:
new MyDomainClass(prop1:"value1", prop2:"value2).save()

This causes and exception upon starting up grails:
Caused by:
org.codehaus.groovy.control.MultipleCompilationErrorsException:
startup failed, Script1.groovy: 26: unable to resolve class
MyDomainClass
@ line 26, column 1.

Seems like, I cannot use domain classes inside groovy migrations. Is
this intentional? Or is GORM code not usable in this context?

2) Since GORM code in migrations seems not to work, I created a
"regular" migration to use the "insert data" target (http://
www.liquibase.org/manual/insert_data). My code:
changeSet(id:'Tag_init', author:'Stefan') {
insert(tableName="tag") {
column(name:"name", value="HURZ")
}
}
The returns an error:
Caused by: groovy.lang.MissingPropertyException: No such property: sql
for class: liquibase.change.InsertDataChange
at liquibase.parser.groovy.GroovyChangeSet.methodMissing
(GroovyChangeSet.groovy:117)
at liquibase.parser.groovy.GroovyChangeSet.invokeMethod
(GroovyChangeSet.groovy)
at Script1$_run_closure1_closure2.doCall(Script1.groovy:4)
at Script1$_run_closure1_closure2.doCall(Script1.groovy)
at liquibase.parser.groovy.GroovyDatabaseChangeLog.changeSet
(GroovyDatabaseChangeLog.groovy:78)
at liquibase.parser.groovy.GroovyDatabaseChangeLog
$changeSet.callCurrent(Unknown Source)
at liquibase.parser.groovy.GroovyDatabaseChangeLog.change

The error seems not being related to my code. Is the "Insert" target
useable with Autobase?

Regards,
Stefan

stefan

unread,
Feb 23, 2009, 9:21:19 AM2/23/09
to Autobase

Robert Fischer

unread,
Feb 23, 2009, 10:55:20 AM2/23/09
to grails-...@googlegroups.com
You can't use domain classes directly in a migration. The problem is that we can't fire up GORM
until after the execution because it might query the database. There may be a way to work around
that, but I haven't figure out how yet. If you've got insight, let me know.

As for this code:
> changeSet(id:'Tag_init', author:'Stefan') {
> insert(tableName="tag") {
> column(name:"name", value="HURZ")
> }
> }

You want insert(tableName:"tag") not insert(tableName="tag").

~~ Robert.
--
~~ Robert Fischer.
Grails Training http://GroovyMag.com/training
Smokejumper Consulting http://SmokejumperIT.com
Enfranchised Mind Blog http://EnfranchisedMind.com/blog

Check out my book, "Grails Persistence with GORM and GSQL"!
http://www.smokejumperit.com/redirect.html

Stefan Armbruster

unread,
Feb 27, 2009, 7:23:48 AM2/27/09
to grails-...@googlegroups.com
Hi,

I think I've just managed to use domain objects in the migration's code. The
trick seems to be really easy. Instead of using groovy.util.Eval for parsing
the migration code I switched to use groovy.lang.GroovyShell for this task.
The latter allows to pass in a custom classloader. If you use
Thread.currentThread().contextClassLoader here, you have access to your domain
classes inside the migration scripts.

Note: I did not dig too far in lbdsl and liquibase code, so I don't know if
this has any unexpected side effects, but it seems to work for me.

Attached there's the tiny patch against
git://github.com/RobertFischer/liquibase-dsl.git. Build lbdsl.jar freshly and
place it into the autobase's lib dir and rebuild&reinstall the Autobase
plugin.

BTW: Wouldn't it be a good idea to place the contents of lbdsl.jar directly in
the Autobase plugin? This might make development a little bit easier.

Regards,
Stefan

lbdsl.patch

Robert Fischer

unread,
Feb 27, 2009, 8:16:40 AM2/27/09
to grails-...@googlegroups.com
Liquibase-DSL is being used outside of Autobase, so it's really a separate project.

What happens if your domain object is out of sync with the database?

Let's say you have a class:

class Foo {
String bar
}

And that's configured properly in the database as a "foo" table with a "bar" field. We then go in
and add a "baz" field which looks like this:

class Foo {
String bar
int baz
}

Now we fire off the migrations.

I'd expect Foo.findByBaz(42) to explode (which I could roll with), but I suspect equally as
explosive is Foo.findByBar("bar") -- since the Hibernate object is expecting a "baz" field to exist,
the generated SQL is going to be bogus.

Between that and the general issues with Groovy in migrations[1], I'm concerned about the torrent of
issues caused by telling people that domain objects can be used -- I'd rather identify some usable
subset of domain objects, or some nice DSL-y ways of dealing with them. Ultimately, the issue with
domain objects in migrations is one of usability, not one of a technical hurdle.

[1] http://wiki.github.com/RobertFischer/autobase/groovy-in-migration-files

~~ Robert.

Stefan Armbruster

unread,
Feb 27, 2009, 10:55:49 AM2/27/09
to grails-...@googlegroups.com
I did not any other GORM methods than "save()" so far in the migration code.
You're right, it's dangerous. My intention was to have a more easy way to
populate "semi-static" data into the db.
E.g. assume a nested categrory tree.
class Category {
String name;
Categroy parent
static hasMany = [children:Category]
}

If the "insert data" migration is used, you need to know the id of the parent
when inserting a child node. Is this possible using the DSL way?
Using GORM syntax, that's pretty easy to populate:
new Category(name:"root").addToChildren(new Category(name:"sub1")).save()

A real problem is what you described in [1], this make me think, I'm on the
wrong way.

Regards,
Stefan

Robert Fischer

unread,
Feb 27, 2009, 11:20:40 AM2/27/09
to grails-...@googlegroups.com
This feels kinda dirty/evil, but I was looking into loading the domain classes as straight POGOs and
injecting our infrastructure, including our own version of some popular GORM calls in a distinct
class loader. So, for instance, "foo.save()" would become an "insert" or "update" call in the
current change set depending on whether "foo" was retrieved by a call or not.

That's a non-trivial amount of work, though, which is why I've been holding off.
Reply all
Reply to author
Forward
0 new messages