Migrations
You define your schema in Java, not in a loose pile of .sql files. Each change is a migration with an up (apply it) and a down (roll it back). Pika tracks which migrations have run, so applying them is safe to do on every startup.
Define migrations
Extend Migrations, override migrations(), and register each change with add(). They run in the order you add them.
import edu.montana.pika.migrations.Migrations;
import edu.montana.pika.migrations.PikaMigration;
public class AppMigrations extends Migrations {
@Override
public void migrations() {
add(this::createUsers);
add(this::addEmailToUsers);
}
public PikaMigration createUsers() {
return makeMigration("001_create_users")
.up("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT
);
""")
.down("DROP TABLE users;");
}
public PikaMigration addEmailToUsers() {
return makeMigration("002_add_email")
.up("ALTER TABLE users ADD COLUMN email TEXT;")
.down("ALTER TABLE users DROP COLUMN email;");
}
}
A migration is a unique name, an up block, a down block, and an optional description(). You write the SQL; Pika runs it. Put several statements in one block by separating them with semicolons, and Pika runs them together in a single transaction.
Watch your target database’s limits. SQLite, for example, cannot DROP COLUMN, so that second down only works where the database supports it.
Apply on startup
Register your migrations on the ORM and call applyMigrations(). Pika runs every migration that has not run yet, in order.
PikaORM orm = new PikaORM("jdbc:sqlite:app.db")
.withMigrations(new AppMigrations())
.applyMigrations();
The first time it runs, Pika creates a pika_migrations table and records each migration’s name as it applies. On later runs it skips anything already recorded, so calling applyMigrations() on every startup is safe.
The console
Auto-applying on startup is convenient but blunt. While developing, boot the interactive console instead and step through migrations by hand.
public static void main(String[] args) {
PikaORM orm = new PikaORM("jdbc:sqlite:app.db").makeDefaultORM();
AppMigrations migrations = new AppMigrations();
orm.withMigrations(migrations);
migrations.console(); // takes over stdin and waits for commands
}
migrations > show
[APPLIED] 001_create_users
[PENDING] 002_add_email
migrations > up
Applying: 002_add_email
| Command | What it does |
|---|---|
show |
List every migration and whether it is applied or pending. |
all |
Apply all pending migrations, in order. |
up |
Apply the next pending migration only. |
down |
Roll back the most recently applied migration (runs its down). |
help |
List the commands. |
exit / quit |
Leave the console. |