Goose for Java
goose4j is an uncomplicated wrapper for the goose sql migration tool's CLI.
This tool doesn't support writing migrations in Go, because if you were writing them in Go then I assume you wouldn't be here reading this.
Migrations are written in plain SQL and goose4j offers a simple API for composing them from Java code in a way that allows you to run the migrations at Java runtime without dealing with how the actual .sql files end up on the filesystem or packaged as JAR resources.
Right now, goose4j supports all of the goose CLI commands and offers structured return of their output for a subset of them. When goose ships JSON output support, goose4j will support structured return data for all commands. But I think what's there right now is a) enough to get going and b) reasonably relaxed parsing that as long as the output doesn't change unrecognizably, new goose versions will probably work as well as we could hope.
Example
Within package com.example.myapp.migrations, we have three migration files:
- v01_InitialMigration.java
- v02_AddBarToFoo.java
- v03_RemoveBarFromFoo.java
public class InitialMigration extends GooseMigration {
@Override
public List<GooseStatement> getMigrations() {
return List.of(
new GooseStatement(
"create table foo(id serial primary key);", // up migration
"drop table foo;" // down migration
),
new GooseStatement(
"insert into foo(id, bar) values (DEFAULT, 'baz'), (DEFAULT, 'qux')" // up-only
)
);
}
}
public class AddBarToFoo extends GooseMigration {
@Override
public List<GooseStatement> getMigrations() {
return List.of(
new GooseStatement(
"alter table foo add column bar text;", // up migration
"alter table foo drop column bar;" // down migration
)
);
}
}
public class AddBarToFoo extends GooseMigration {
@Override
public List<GooseStatement> getMigrations() {
return List.of(
new GooseStatement(
"alter table foo drop column bar;",
"alter table foo add column bar text;"
)
);
}
}
And in com.example.myapp.MyApp we use them:
public class MyApp {
public static void main(String ... args) {
Goose goose = new Goose(
"postgres",
"host=" + postgres.getHost() +
" user=" + postgres.getUsername() +
" dbname=" + postgres.getDatabaseName() +
" port=" + postgres.getFirstMappedPort() +
" password=" + postgres.getPassword()
);
goose.add(new v01_InitialMigration());
goose.add(new v02_AddBarToFoo());
goose.add(new v03_RemoveBarFromFoo());
goose.up();
}
}
Manual goose use with goose4j
If you want to monkey around manually, you have two options:
- Use
Goose::command()if you are building a programmatic wrapper. - Add a CLI option to your program that makes it call
Goose::writeMigrationFiles()to some directory. This will dump (and not clean up!) migration files in the way the goose CLI is expecting them, allowing you to use the goose command like normal.
Migration Class Names
You can name your migration classes whatever you want. But if you follow the convention shown above in the examples, you
will find that your migrations display in order in your IDE, and you don't have to override GooseMigration::getVersion().
And of course if you don't follow that naming convention, you DO need to override GooseMigration::getVersion().
And if you get overly creative with anonymous classes or things of that nature, you may need to override getFileName and getMigrationName so I suggest you not do that.
