Migrations
Managing database schema changes is a critical part of application development. Exposed offers several tools to help with schema migrations, allowing you to evolve your database alongside your codebase.
While Exposed provides basic migration support through SchemaUtils, the MigrationUtils methods from either the exposed-migration-jdbc or exposed-migration-r2dbc packages provide a more structured and production-ready way to manage schema changes. They allow you to inspect differences between the current database state and your defined table schema and to generate or apply migration scripts accordingly.
Adding dependencies
To use the methods provided by MigrationUtils, include the following dependencies in your build script:
exposed-migration-core, containing core common functionality for database schema migrations.A dependency for migration support with either a JDBC or R2DBC driver.
Aligning the database schema
When you need to bring your database schema in line with your current Exposed table definitions, you have three options:
Generate missing column statements
If you only need the SQL statements that create any columns that are missing from the existing tables in the database, use the SchemaUtils.addMissingColumnsStatements() function:
This function returns a collection of string SQL statements, ensuring that any column-associated constraints are aligned. As it adds missing columns, it simultaneously adds any associated constraints such as primary keys, indexes, and foreign keys that may be absent.
Generate all required statements
To compare your live database schema against your current Exposed table definitions and generate all statements required to align the two, use the MigrationUtils.statementsRequiredForDatabaseMigration() function:
The returned collection of string SQL statements may include CREATE, ALTER, and DROP operations — including potentially destructive actions like DROP COLUMN or DELETE, so review them carefully before choosing to execute them.
Generate a migration script
To generate a migration script based on schema differences between your database and the current Exposed model, use the MigrationUtils.generateMigrationScript() function:
This method allows you to see what the migration script will look like before applying the migration. If a migration script with the same name already exists, its content will be overwritten.
Validating the database schema
Before applying any migrations, it's useful to validate that your Exposed schema definitions match the actual state of the database. While the primary use of schema alignment methods is to generate SQL statements and migration scripts, these same methods can also serve as pre-checks — especially when used to detect unexpected changes.
Exposed provides several low-level APIs that support schema validation and can be integrated into custom migration or deployment pipelines. These methods are also used internally by Exposed to generate migration statements, but you can also use them for more precise checks.
Check for existence of a database object
To determine if a specific database object is already present, use the .exists() method on a Table, Sequence, or Schema.
Structural integrity checks
To evaluate whether a table has excessive indices or foreign keys, which might indicate schema drift or duplication, use one of the following SchemaUtils methods:
Database metadata inspection
To retrieve metadata from the current dialect to compare with your defined Exposed schema, use one of the following currentDialectMetadata methods:
Legacy columns cleanup
As your schema evolves, it's common to remove or rename columns in your table definitions. However, old columns may still exist in the database unless explicitly dropped.
The MigrationUtils.dropUnmappedColumnsStatements() function helps identify columns that are no longer present in your current table definitions and returns the SQL statements to remove them:
For indices and sequences, you can use the MigrationUtils.dropUnmappedIndices() and MigrationUtils.dropUnmappedSequences() methods.
Logging
By default, each method provided by MigrationUtils logs descriptions and the execution time of each intermediate step. These logs are emitted at the INFO level and can be disabled by setting withLogs to false:
Limitations
While Exposed's migration tools are powerful, there are some limitations:
You must still implement and manage your own migration flow.
Automatic migration execution is not provided — scripts must be run manually or integrated into your deployment process. This limitation is already addressed in the Gradle migration plugin feature request.
Some database-specific behaviors, such as SQLite's limited
ALTER TABLEsupport, may lead to partial or failed migrations if not reviewed.Destructive operations like
DROP COLUMNorDROP SEQUENCEcan be included — caution is advised.
We recommend that you always manually review generated diffs or scripts before applying them to a live database.
SQLite
SQLite has strict limitations around the ALTER TABLE ADD COLUMN statement. For example, it does not allow adding a new column without a default value under certain conditions. Since Exposed cannot account for all of SQLite’s specific constraints, it will still generate the expected SQL statement. It is up to you to review the generated SQL and avoid attempting migrations that are incompatible with SQLite’s rules. If such a statement is executed, it will fail at runtime.
PostgreSQL
When running on PostgreSQL, the functions to align the database schema also check for inconsistencies between table definitions and sequences (especially those tied to SERIAL columns on IdTable).
Sequences manually created with CREATE SEQUENCE and not linked to a table are ignored. No DROP statements are generated for such sequences.
Feature requests
Gradle plugin
A Gradle plugin to simplify SQL migrations is in development. A proposed design for Flyway integration has been presented and is actively being implemented. To show interest or get involved, see the YouTrack issue for creating the migration Gradle plugin.
Maven and Liquibase integration
Exposed does not currently offer a Maven plugin or Liquibase integration — share your interest to help shape future support: