Database migrations in Grails

When you make changes to a domain class (like adding an extra field) in your Grails application, and then deploy to production, that change is usually propagated out to the corresponding table in the database. That you don't have to manually update the structure of your table is thanks to some Hibernate magic, but this magic in not always enough. It is (rightly so) conservative in nature, and it won't perform a potentially high risk change - such as the dropping of a column - automagically. So you are left with two options. The first is to perform these higher risk changes manually, and the second is to start using the database migrations plugin.

Introduction

This plugin is part of a new set of integral plugins (like resources) maintained by the leaders of the Grails community. There are already some useful articles on using the database migration plugin, but one more can't hurt! Like many aspects of the Grails ecosystem, once you grok how to use this plugin, you will see just how easy and elegant it is to use.
Built on the Liquibase library, it is easier to use that its forefathers - the liquibase and autobase plugins. Just like its predecessors the database migration plugin is based on one or more changelog files. These changelog files can be in the original Liquibase XML format or the more Grails like groovy format. Needless to say, the latter is the more popular choice. Here is an example of what the contents of a changelog file might look like:
databaseChangeLog = {
    changeSet(author: "eamonnoconnell (generated)", id: "1346839043312-1") {
    renameTable(oldTableName:'user_account', newTableName:'person')

    addColumn(tableName:"person"){
      column(name: "account_expired", type: "bit") {
    constraints(nullable: "false")
   }
   column(name: "account_locked", type: "bit") {
    constraints(nullable: "false")
   }
   column(name: "created_date", type: "datetime") {
    constraints(nullable: "false")
   }
   column(name: "enabled", type: "bit") {
    constraints(nullable: "false")
   }
   column(name: "password_expired", type: "bit") {
    constraints(nullable: "false")
   }
    }
    renameColumn(tableName:"person", oldColumnName:"email", 
                 newColumnName:"username", columnDataType: "varchar(255)")
  }
}
Checkout the Liquibase website for a reference list of all the possible database refactoring commands you can define in your changelog.

Getting started

The following are a list of steps that should get you up and running in no time!

1. Add database migration plugin to your application

First ensure that the plugin is included appropriately in your BuildConfig.groovy.
plugins {
  ...
    runtime ":database-migration:1.1"  
  ...
}
Warning: Be sure to practice on a copy of your production database, and take a backup of your production database just prior to applying the changes to it.

2. Prevent Hibernate from modifying your database

As mentioned previously, even without the migration plugin Grails can still modify your database to reflect changes to your domain classes. This is done by specifing in your datasource config that dbCreate="update". The database migration plugin and the Hibernate database update magic are mutually exclusive. If you go with the migration plugin, you need to turn off the Hibernate magic feature. You can easily do so by updating your datasource config like this:
dataSource {
...
dbCreate=""
...
}

3. Generate a root changelog file

Now you need to create the root changelog file changelog.groovy:
dbm-create-changelog

4. Generate your initial changelog file

This file will contain the commands to change the schema of a blank database into what your current database has. This change-log will reflect the database as it currently is. You can generate this change log by one of two ways depending on your current situation. If your domain classes and production database are in-sync you can generate it from the domain classes. If however they are not, you need to generate them from a copy of your production database.
To create your starting change log from the database, put a copy of your database on your local machine, and connect your application to it. Then run the following command from the grails console:
dbm-generate-changelog --add changelog-1.0.groovy
If your database and domain classes are still in sync then this step is a little easier and you can generate your first change log file from your domain classes with the following command:
dbm-generate-gorm-changelog --add changelog-1.0.groovy
So now you should have the following:
migrations/changelog.groovy - a root file containing references to all other changelog files. changelog-1.0.groovy - your first changelog file, that should get you from a blank DB to what you currently have in production

5. Configure your database

In order not to apply changes over and over again, Dbm stores a record of each changeset applied in a table on the database it applied the change set to. So in order to configure Dbm to not apply the initial changelog generated from the database to that same database run the following command:
dbm-changelog-sync

6. Find the differences between your database and your domain classes

In order to avoid having to write every change to your database manually, Dbm provides a diff command that will generate a list of change sets it thinks should be applied to bring your database and domain classes in-sync. Keep things clean and put these changesets in a new file with the following command:
dbm-gorm-diff --add changelog-1.0.1.groovy
Note: The dbm-gorm-diff command is great way of automatically determining the database changes required, but it is not fool proof. You might find you have to make adjustments to the generated changelog file.

7. Execute your changelogs to bring your database in line with your domain classes

Now that you have determined what changes must be made to your tables, you can now apply them. You do so with the following command:
dbm-update

Note:

Something that caught me out was that by default the database migration plugin doesn't auto-run in test. So let's say you want to run tests against a database including the building of that database using your changelogs, then you need to define the following in your config:
grails.plugin.databasemigration.autoMigrateScripts = ['RunApp', 'TestApp']

Comments

  1. discipleship training The C.S. Lewis Institute was founded in 1976 and endeavors, in the legacy of C.S. Lewis, to develop disciples who will articulate, defend and live their faith in Christ in personal and public life

    ReplyDelete

Post a Comment

Popular posts from this blog

AngularJs: User friendly date display with AngularJs and MomentJs

Getting started with Grails functional tests using Geb + Spock

Nerd Tree: A File Explorer with Mac Vim