Dropwizard Migrations

The dropwizard-migrations module provides you with a wrapper for Liquibase database refactoring.

Configuration

Like Dropwizard JDBI3, your configuration class needs a DataSourceFactory instance:

public class ExampleConfiguration extends Configuration {
    @Valid
    @NotNull
    private DataSourceFactory database = new DataSourceFactory();

    @JsonProperty("database")
    public DataSourceFactory getDataSourceFactory() {
        return database;
    }
}

Adding The Bundle

Then, in your application’s initialize method, add a new MigrationsBundle subclass:

@Override
public void initialize(Bootstrap<ExampleConfiguration> bootstrap) {
    bootstrap.addBundle(new MigrationsBundle<ExampleConfiguration>() {
        @Override
        public DataSourceFactory getDataSourceFactory(ExampleConfiguration configuration) {
            return configuration.getDataSourceFactory();
        }
    });
}

If you are using Dropwizard Hibernate or Dropwizard JDBI3 in your application, you can use these techniques within a CustomChange to make bigger data migrations. Therefore you need to provide an instance of these to the MigrationsBundle like in the following example:

@Override
public void initialize(Bootstrap<ExampleConfiguration> bootstrap) {
    bootstrap.addBundle(new MigrationsBundle<ExampleConfiguration>() {
        @Override
        public DataSourceFactory getDataSourceFactory(ExampleConfiguration configuration) {
            return configuration.getDataSourceFactory();
        }

        @Override
        public Map<String, Object> getScopedObjects() {
            Map<String, Object> scopedObjects = new HashMap<>();
            scopedObjects.put("hibernateSessionFactory", hibernateBundle.getSessionFactory());
            return scopedObjects;
        }
    });
}

In your CustomChange you can retrieve the registered SessionFactory with this code:

public void execute(Database database) throws CustomChangeException {
    Scope.getCurrentScope().get("hibernateSessionFactory", SessionFactory.class);
}

Defining Migrations

Your database migrations are stored in your Dropwizard project, in src/main/resources/migrations.xml. This file will be packaged with your application, allowing you to run migrations using your application’s command-line interface. You can change the name of the migrations file used by overriding the getMigrationsFileName() method in MigrationsBundle.

For example, to create a new people table, you might create an initial migrations.xml like this:

<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

    <changeSet id="1" author="codahale">
        <createTable tableName="people">
            <column name="id" type="bigint" autoIncrement="true">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="fullName" type="varchar(255)">
                <constraints nullable="false"/>
            </column>
            <column name="jobTitle" type="varchar(255)"/>
        </createTable>
    </changeSet>
</databaseChangeLog>

For more information on available database refactorings, check the Liquibase documentation.

Checking Your Database’s State

To check the state of your database, use the db status command:

java -jar hello-world.jar db status helloworld.yml

Dumping Your Schema

If your database already has an existing schema and you’d like to pre-seed your migrations.xml document, you can run the db dump command:

java -jar hello-world.jar db dump helloworld.yml

This will output a Liquibase change log with a changeset capable of recreating your database.

Tagging Your Schema

To tag your schema at a particular point in time (e.g., to make rolling back easier), use the db tag command:

java -jar hello-world.jar db tag helloworld.yml 2012-10-08-pre-user-move

Migrating Your Schema

To apply pending changesets to your database schema, run the db migrate command:

java -jar hello-world.jar db migrate helloworld.yml

Warning

This will potentially make irreversible changes to your database. Always check the pending DDL scripts by using the --dry-run flag first. This will output the SQL to be run to stdout.

Note

To apply only a specific number of pending changesets, use the --count flag.

Rolling Back Your Schema

To roll back changesets which have already been applied, run the db rollback command. You will need to specify either a tag, a date, or a number of changesets to roll back to:

java -jar hello-world.jar db rollback helloworld.yml --tag 2012-10-08-pre-user-move

Warning

This will potentially make irreversible changes to your database. Always check the pending DDL scripts by using the --dry-run flag first. This will output the SQL to be run to stdout.

Testing Migrations

To verify that a set of pending changesets can be fully rolled back, use the db test command, which will migrate forward, roll back to the original state, then migrate forward again:

java -jar hello-world.jar db test helloworld.yml

Warning

Do not run this in production, for obvious reasons.

Preparing A Rollback Script

To prepare a rollback script for pending changesets before they have been applied, use the db prepare-rollback command:

java -jar hello-world.jar db prepare-rollback helloworld.yml

This will output a DDL script to stdout capable of rolling back all unapplied changesets.

Generating Documentation

To generate HTML documentation on the current status of the database, use the db generate-docs command:

java -jar hello-world.jar db generate-docs helloworld.yml ~/db-docs/

Dropping All Objects

To drop all objects in the database, use the db drop-all command:

java -jar hello-world.jar db drop-all --confirm-delete-everything helloworld.yml

Warning

You need to specify the --confirm-delete-everything flag because this command deletes everything in the database. Be sure you want to do that first.

Fast-Forwarding Through A Changeset

To mark a pending changeset as applied (e.g., after having backfilled your migrations.xml with db dump), use the db fast-forward command:

java -jar hello-world.jar db fast-forward helloworld.yml

This will mark the next pending changeset as applied. You can also use the --all flag to mark all pending changesets as applied.

Support For Adding Multiple Migration Bundles

Assuming migrations need to be done for two different databases, you would need to have two different data source factories:

public class ExampleConfiguration extends Configuration {
    @Valid
    @NotNull
    private DataSourceFactory database1 = new DataSourceFactory();

    @Valid
    @NotNull
    private DataSourceFactory database2 = new DataSourceFactory();

    @JsonProperty("database1")
    public DataSourceFactory getDb1DataSourceFactory() {
        return database1;
    }

    @JsonProperty("database2")
    public DataSourceFactory getDb2DataSourceFactory() {
        return database2;
    }
}

Now multiple migration bundles can be added with unique names like so:

@Override
public void initialize(Bootstrap<ExampleConfiguration> bootstrap) {
    bootstrap.addBundle(new MigrationsBundle<ExampleConfiguration>() {
        @Override
        public DataSourceFactory getDataSourceFactory(ExampleConfiguration configuration) {
            return configuration.getDb1DataSourceFactory();
        }

        @Override
        public String name() {
            return "db1";
        }
    });

    bootstrap.addBundle(new MigrationsBundle<ExampleConfiguration>() {
        @Override
        public DataSourceFactory getDataSourceFactory(ExampleConfiguration configuration) {
            return configuration.getDb2DataSourceFactory();
        }

        @Override
        public String name() {
            return "db2";
        }
    });
}

To migrate your schema:

java -jar hello-world.jar db1 migrate helloworld.yml

and

java -jar hello-world.jar db2 migrate helloworld.yml

Note

Whenever a name is added to a migration bundle, it becomes the command that needs to be run at the command line. eg: To check the state of your database, use the status command:

java -jar hello-world.jar db1 status helloworld.yml

or

java -jar hello-world.jar db2 status helloworld.yml

By default the migration bundle uses the “db” command. By overriding you can customize it to provide any name you want and have multiple migration bundles. Wherever the “db” command was being used, this custom name can be used.

There will also be a need to provide different change log migration files as well. This can be done as

java -jar hello-world.jar db1 migrate helloworld.yml --migrations <path_to_db1_migrations.xml>
java -jar hello-world.jar db2 migrate helloworld.yml --migrations <path_to_db2_migrations.xml>

More Information

If you are using databases supporting multiple schemas like PostgreSQL, Oracle, or H2, you can use the optional --catalog and --schema arguments to specify the database catalog and schema used for the Liquibase commands.

For more information on available commands, either use the db --help command, or for more detailed help on a specific command, use db <cmd> --help.