Sample app for Managing Your Database Schema with the Grails Database Migration Plugin (Grails 8) (Apache Grails 8.0.0-SNAPSHOT, JDK 21).
The guide walks through evolving a PostgreSQL schema safely across releases with Liquibase changelogs: baseline the schema from GORM domains, make columns nullable, add columns, redesign tables, and migrate existing data with custom SQL - without relying on Hibernate dbCreate auto-DDL.
This is the production follow-on to the grails-data-access guide: "Now that I've modeled my data, how do I evolve the schema safely across releases?"
| Directory | What it is |
|---|---|
initial/ |
Grails 8 REST API starter from start.grails.org (postgres, database-migration, views-json, testcontainers, spock). Contains example.Person with dbCreate: none - work through the guide starting here. |
complete/ |
The fully migrated sample - Person and Address domains, Liquibase changelogs (baseline → nullable age → address columns on person → split into Address + data migration SQL), GET /api/persons, and Spock specs. |
git clone -b grails8 https://github.com/grails-guides/grails-database-migration.git
cd grails-database-migration/complete
./gradlew test integrationTestTo follow the guide step by step, start from initial/:
cd grails-database-migration/initial
./gradlew testRun either app locally (both require PostgreSQL on localhost:5432, database devDb):
cd grails-database-migration/initial # after working through migrations in the guide
./gradlew bootRun
# or the finished sample:
cd grails-database-migration/complete
./gradlew bootRunExample endpoint: GET /api/persons.
The integration spec (DatabaseMigrationIntegrationSpec) asserts on a clean Testcontainers PostgreSQL database that Liquibase tracking tables exist, person and address have the expected schema (via JDBC DatabaseMetaData), GORM persistence works after migrations apply, and a migration-path test proves legacy address columns are copied into address before they are dropped.
- JDK 21 (Temurin recommended; the Gradle build enforces Java 21+)
- Docker running locally - required for
integrationTest(Testcontainers PostgreSQL). Unit tests (./gradlew test) do not need Docker. - PostgreSQL 16 on
localhost:5432- required for./gradlew bootRunin bothinitial/andcomplete/(default databasedevDbinapplication.yml)
If Gradle reports "Run this build using a Java 21 or newer JVM", your shell or IDE is still on an older JDK:
sdk install java 21.0.6-tem
sdk default java 21.0.6-tem
java -version # should show 21.xIf integrationTest fails with a Testcontainers / docker.sock error, start Docker Desktop (or your local Docker daemon) and retry.
From either initial/ or complete/:
# Generate initial changelog from GORM domains
./grailsw dbm-generate-gorm-changelog changelog.groovy
# Diff domain changes → new migration file
./grailsw dbm-gorm-diff change-age-constraint-to-nullable.groovy --add
# Inspect / preview / apply (release runbook)
./grailsw dbm-status
./grailsw dbm-update-sql pending.sql
./grailsw dbm-update
# Roll back the last reversible changeset
./grailsw dbm-rollback-count 1
# Tag a known-good point, preview, and roll back to it
./grailsw dbm-tag before-address-redesign
./grailsw dbm-rollback-sql before-address-redesign
./grailsw dbm-rollback before-address-redesignIf ./grailsw is unavailable, use Gradle (CI/automation fallback):
./gradlew runCommand "-Pargs=dbm-update"# application.yml - never auto-DDL when teaching migrations
environments:
development:
dataSource:
dbCreate: none
url: jdbc:postgresql://localhost:5432/devDb?tcpKeepAlive=true
grails:
plugin:
databasemigration:
updateOnStart: true
updateOnStartFileName: changelog.groovy// grails-app/migrations/changelog.groovy
databaseChangeLog = {
include file: 'create-person-table.groovy'
include file: 'change-age-constraint-to-nullable.groovy'
include file: 'add-address-fields-to-person.groovy'
include file: 'create-address-table.groovy'
}Grails 8 uses org.apache.grails:grails-data-hibernate5-dbmigration from the Grails BOM - not the legacy org.grails.plugins:database-migration coordinate.
Published narrative lives on grails.apache.org/guides in apache/grails-static-website under guides/grails-database-migration/v8/.
GitHub Actions (.github/workflows/grails8.yml) runs ./gradlew test for initial and complete, and ./gradlew integrationTest for complete, on pushes and PRs to the grails8 branch.