Interface DatabaseMigrator

All Known Implementing Classes:
BaseDatabaseMigrator

public interface DatabaseMigrator
Interface used to migrate database content across schema versions. The migration process makes a copy of the highest available schema database, and then runs the migration steps from that schema version to the current version. The old database remains on disk in case of rollback.

This method of migration means that not all schemas are represented by a database on disk. For example, if the only database on disk is schema 0 and the current schema is 3, then the database will be copied, tagged as schema 3 and upgraded. There will not be a copy of schema 1 or 2 on disk.

This interface allows for purging old schemas. Since every migration makes another copy of the database, this interface uses the retention schema to determine what versions of the database are safe to delete.

Database Migration

The database migration process ensures that the database schema matches what the code expects. This is executed every time the application starts. During this phase, the migrator does one of the following:
  • The database file does not exist, so the migrator creates it from scratch.
  • The code's schema version is newer than the database's, so the migrator updates the database.
  • The code's schema version is older than the database's, so the migrator rolls back to a previous version.
  • The code's schema version matches the database's schema version, so all is good: no action is taken.
Updating the database to a newer version is also called "upgrading" or "rolling forward".

Downgrading the database to an older version is also called "rolling back" or "rollback".

Example

In the following example, the schema #0 code creates a table named "options", while the schema #1 code renames an existing field and adds a new field.

 
  public class OurDatabaseMigrator implements DatabaseMigrator {

      private Jdbi jdbi;

      @Override
      public int getCurrentSchema() {
          return 1;
      }

      @Override
      public void setJdbi(Jdbi jdbi) {
          this.jdbi = jdbi;
      }

      @Override
      public void migrateToSchema(Jdbi jdbi, int schema) {

          switch (schema) {

              // Initial schema version #0:
              case 0 -> jdbi.useHandle(handle -> {
                  // Create the "options" table:
                  handle.execute("""
                      CREATE TABLE options(
                      [name]  TEXT NOT NULL,
                      [value] TEXT,
                      PRIMARY KEY([name]))""");
              });

              // Current schema version #1:
              case 1 -> jdbi.useHandle(handle -> {
                  // Change column name from [value] to [currentValue]:
                  handle.execute("""
                      ALTER TABLE options
                      RENAME COLUMN [value] TO [currentValue]""");
                  // Add column [defaultValue]:
                  handle.execute("""
                      ALTER TABLE options
                      ADD COLUMN [defaultValue] TEXT""");
              });

              default -> throw new ConfigException(
                  String.format("Invalid schema version [%s] encountered", schema));
          }
      }
  }
 
 
Since:
1.0
Version:
2022-08-30
  • Method Summary

    Modifier and Type
    Method
    Description
    int
    Returns the current schema version.
    default int
    Returns the minimum schema version to use for migration.
    default int
    Returns the minimum schema version to keep on disk after migration.
    default void
    migrateToSchema(DataSource datasource, int schema)
    Applies any changes to the database for the specified schema number.
    void
    migrateToSchema(Jdbi jdbi, int schema)
    Applies any changes to the database for the specified schema number.
    default boolean
    Returns true if any schema versions newer than the current version should be deleted from disk.
    default void
    Sets the final DataSource to use after migration is complete.
    default void
    setJdbi(Jdbi jdbi)
    Sets the final Jdbi instance to use after migration is complete.
  • Method Details

    • getCurrentSchema

      int getCurrentSchema()
      Returns the current schema version. The migration process migrates to this version during startup. Must be a non-negative integer that starts with zero, then increments by one each time the schema is updated.

      Example

      1. Database schema versions 4, 6, and 7 exist on the disk.
      2. You set the desired schema by having this method return the number 9.
      3. After migration, the database with schema #9 exists on the disk, however a database with schema #8 does not exist.
      Returns:
      the current schema version
    • getMinimumMigrationSchema

      default int getMinimumMigrationSchema()
      Returns the minimum schema version to use for migration. Any existing database with this version or higher can be used for migration. Anything below this version will be ignored, causing migration to create a clean schema with no data.

      Example

      1. Database schema versions 0, 1, and 2 exist on the disk.
      2. You set the minimum migration schema by having this method return the number 4.
      3. No database migration takes place because all existing schemas are less than 4.
      4. After migration, the database with schema 4 exists, but it contains no data.
      Returns:
      the minimum schema to migrate
    • getMinimumRetentionSchema

      default int getMinimumRetentionSchema()
      Returns the minimum schema version to keep on disk after migration. Any database with a schema older than this will be deleted from disk.

      Example

      1. Database schema versions 0, 1, 2, 4, 6, and 7 exist on the disk.
      2. You migrate to the newest schema, version #8.
      3. This method in your custom data source migrator returns the number 4.
      4. After migration, databases with schemas 0, 1, and 2 have been deleted, leaving databases 4, 6, 7, and 8 on the disk.
      Returns:
      minimum retention schema
    • purgeNewerSchema

      default boolean purgeNewerSchema()
      Returns true if any schema versions newer than the current version should be deleted from disk. In the event of a rollback, this will cause the newer data to be deleted, so when rolling forward the data will be migrated again.

      Example 1 (purge newer)

      1. Database schema versions 0, 1, 2, 4, 6, and 7 exist on the disk.
      2. You want newer schemas deleted, so you have this method return true .
      3. You migrate/rollback to an older schema, version #6.
      4. After migration, the database with schema #7 has been deleted, and the active database schema is #6.

      Example 2 (keep newer)

      1. Database schema versions 0, 1, 2, 4, 6, and 7 exist on the disk.
      2. You do not want newer schemas deleted, so you have this method return false .
      3. You migrate/rollback to an older schema, version #6.
      4. After migration, the database with schema #7 still exists, even though the active database schema is #6.
      Returns:
      true to purge newer schemas on rollback, or false to keep
    • migrateToSchema

      default void migrateToSchema(DataSource datasource, int schema)
      Applies any changes to the database for the specified schema number. This is called for every schema version from the current database schema plus 1 to the current schema. For example, if the current database is at schema 2 and the current schema is 4, then this will be called with a schema of 3 and then 4. The DataSource provided may be temporary and should only be used for migration. When migration is complete, a call to setDataSource() will be made with the final DataSource .

      By default this simply calls the JDBI version of this call. Override this to use the DataSource directly instead of JDBI .

      Parameters:
      datasource - the datasource to the database to migrate
      schema - the schema version to apply
    • migrateToSchema

      void migrateToSchema(Jdbi jdbi, int schema)
      Applies any changes to the database for the specified schema number. This is called for every schema version from the current database schema plus 1 to the current schema. For example, if the current database is at schema 2 and the current schema is 4, then this will be called with a schema of 3 and then 4. The Jdbi provided may be temporary and should only be used for migration. When migration is complete, a call to setJdbi() will be made with the final Jdbi instance.
      Parameters:
      jdbi - Jdbi wrapper for the datasource to use for migration
      schema - the schema version to apply
    • setDataSource

      default void setDataSource(DataSource datasource)
      Sets the final DataSource to use after migration is complete. Override to gain access to the underlying DataSource instead of the Jdbi instance.
    • setJdbi

      default void setJdbi(Jdbi jdbi)
      Sets the final Jdbi instance to use after migration is complete. Override to gain access to the final Jdbi instance if the migrator is also the DAO object.