Migrazioni di database con Flyway

1. Introduzione

Questo articolo descrive i concetti chiave di Flyway e come possiamo utilizzare questo framework per rimodellare continuamente lo schema del database della nostra applicazione in modo semplice e affidabile. Alla fine, presenteremo un esempio di gestione di un database H2 in memoria utilizzando un plugin Maven Flyway.

Flyway aggiorna un database da una versione all'altra utilizzando le migrazioni. Possiamo scrivere migrazioni in SQL con sintassi specifica per database o in Java per trasformazioni database avanzate.

Le migrazioni possono essere con versione o ripetibili. Il primo ha una versione unica e viene applicato esattamente una volta. Quest'ultimo non ha una versione. Invece, vengono (ri) applicati ogni volta che il loro checksum cambia.

All'interno di una singola esecuzione di migrazione, le migrazioni ripetibili vengono sempre applicate per ultime, dopo che sono state eseguite le migrazioni con versione in sospeso. Le migrazioni ripetibili vengono applicate in base alla loro descrizione. Per una singola migrazione, tutte le istruzioni vengono eseguite all'interno di una singola transazione di database.

In questo articolo, ci concentriamo principalmente su come possiamo utilizzare il plug-in Maven per eseguire le migrazioni del database.

2. Plugin Flyway Maven

Per installare un plugin Flyway Maven, aggiungiamo la seguente definizione di plugin al nostro pom.xml:

 org.flywaydb flyway-maven-plugin 4.0.3 

Possiamo controllare l'ultima versione del plugin disponibile su Maven Central.

Questo plugin Maven può essere configurato in quattro modi diversi. Fare riferimento alla documentazione per ottenere un elenco di tutte le proprietà configurabili.

2.1. Configurazione plug-in

Possiamo configurare il plugin direttamente tramite il tag nella definizione del plugin del nostro pom.xml:

 org.flywaydb flyway-maven-plugin 4.0.3  databaseUser databasePassword  schemaName  ...  

2.2. Proprietà Maven

Possiamo anche configurare il plugin specificando proprietà configurabili come proprietà Maven nel nostro pom:

 ...  databaseUser databasePassword schemaName ...  ... 

2.3. File di configurazione esterno

Possiamo anche fornire la configurazione del plug-in in un file .properties separato :

flyway.user=databaseUser flyway.password=databasePassword flyway.schemas=schemaName ...

Il nome del file di configurazione predefinito è flyway.properties e dovrebbe risiedere nella stessa directory del file pom.xml . La codifica è specificata da flyway.encoding (il valore predefinito è UTF-8 ).

Se stai utilizzando qualsiasi altro nome (ad esempio customConfig.properties ) come file di configurazione, allora dovrebbe essere specificato esplicitamente quando si richiama il comando Maven:

$ mvn -Dflyway.configFile=customConfig.properties

2.4. Proprietà di sistema

Infine, tutte le proprietà di configurazione possono anche essere specificate come proprietà di sistema quando si richiama Maven sulla riga di comando:

$ mvn -Dflyway.user=databaseUser -Dflyway.password=databasePassword -Dflyway.schemas=schemaName

Di seguito è riportato un ordine di precedenza quando una configurazione viene specificata in più di un modo:

  1. Proprietà di sistema
  2. File di configurazione esterno
  3. Proprietà Maven
  4. Configurazione del plugin

3. Esempio di migrazione

In questa sezione, esaminiamo i passaggi necessari per migrare uno schema di database in un database H2 in memoria utilizzando il plug-in Maven. Usiamo un file esterno per configurare Flyway.

3.1. Aggiorna POM

Innanzitutto, aggiungiamo H2 come dipendenza:

 com.h2database h2 1.4.196 

Possiamo, ancora una volta, controllare l'ultima versione del driver disponibile su Maven Central. Aggiungeremmo anche il plug-in Flyway come spiegato in precedenza.

3.2. Configura Flyway utilizzando un file esterno

Successivamente, creiamo myFlywayConfig.properties in $ PROJECT_ROOT con il seguente contenuto:

flyway.user=databaseUser flyway.password=databasePassword flyway.schemas=app-db flyway.url=jdbc:h2:mem:DATABASE flyway.locations=filesystem:db/migration

La configurazione precedente specifica che i nostri script di migrazione si trovano nella directory db / migration . Si connette a un'istanza H2 in memoria utilizzando databaseUser e databasePassword .

Lo schema del database dell'applicazione è app-db .

Ovviamente, sostituiamo flyway.user, flyway.password e flyway.url con il nostro nome utente del database, password del database e URL del database in modo appropriato.

3.3. Definisci prima migrazione

Flyway aderisce alla seguente convenzione di denominazione per gli script di migrazione:

__. sql

Dove:

  • – Default prefix is V, which may be configured in the above configuration file using the flyway.sqlMigrationPrefix property.
  • – Migration version number. Major and minor versions may be separated by an underscore. The migration version should always start with 1.
  • – Textual description of the migration. The description needs to be separated from the version numbers with a double underscore.

Example: V1_1_0__my_first_migration.sql

So, let's create a directory db/migration in $PROJECT_ROOT with a migration script named V1_0__create_employee_schema.sql containing SQL instructions to create the employee table:

CREATE TABLE IF NOT EXISTS `employee` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(20), `email` varchar(50), `date_of_birth` timestamp )ENGINE=InnoDB DEFAULT CHARSET=UTF8;

3.4. Execute Migrations

Next, we invoke the following Maven command from $PROJECT_ROOT to execute database migrations:

$ mvn clean flyway:migrate -Dflyway.configFile=myFlywayConfig.properties

This should result in our first successful migration.

The database schema should now be depicted as follows:

employee: +----+------+-------+---------------+ | id | name | email | date_of_birth | +----+------+-------+---------------+

We can repeat definition and execution steps to do more migrations.

3.5. Define and Execute Second Migration

Let's see what a second migration looks like by creating a second migration file with name V2_0_create_department_schema.sql containing the following two queries:

CREATE TABLE IF NOT EXISTS `department` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(20) )ENGINE=InnoDB DEFAULT CHARSET=UTF8; ALTER TABLE `employee` ADD `dept_id` int AFTER `email`;

We'll execute a similar migration like we did the first time.

And now, our database schema has changed to add a new column to employee and a new table:

employee: +----+------+-------+---------+---------------+ | id | name | email | dept_id | date_of_birth | +----+------+-------+---------+---------------+
department: +----+------+ | id | name | +----+------+

We may now verify that both migrations were indeed successful by invoking the following Maven command:

$ mvn flyway:info -Dflyway.configFile=myFlywayConfig.properties

4. Disabling Flyway in Spring Boot

Sometimes we may need to disable Flyway migrations under certain circumstances.

For example, it's a common practice to generate database schema based on the entities during tests. In such a situation, we can disable Flyway under the test profile.

Let's see how easy it is in Spring Boot.

4.1. Spring Boot 1.x

All we need to do is to set the flyway.enabled property in our application-test.properties file:

flyway.enabled=false

4.2. Spring Boot 2.x

In the more recent versions of Spring Boot, this property has been changed to spring.flyway.enabled:

spring.flyway.enabled=false

4.3 Empty FlywayMigrationStrategy

If we only want to disable automatic Flyway migration on startup, but still be able to trigger the migration manually, then using the properties described above isn't a good choice.

That's because in such a situation Spring Boot will not auto-configure the Flyway bean anymore. Consequently, we'd have to provide it on our own which isn't very convenient.

So if this is our use case, we can leave Flyway enabled and implement an empty FlywayMigrationStrategy:

@Configuration public class EmptyMigrationStrategyConfig { @Bean public FlywayMigrationStrategy flywayMigrationStrategy() { return flyway -> { // do nothing }; } }

This will effectively disable Flyway migration on application startup.

But we'll still be able to trigger the migration manually:

@RunWith(SpringRunner.class) @SpringBootTest public class ManualFlywayMigrationIntegrationTest { @Autowired private Flyway flyway; @Test public void skipAutomaticAndTriggerManualFlywayMigration() { flyway.migrate(); } }

5. How Flyway Works

To keep track of which migrations have already been applied, when and by whom, it adds a special bookkeeping table to your schema. This metadata table also tracks migration checksums and whether or not the migrations were successful.

The framework performs the following steps to accommodate evolving database schemas:

  1. It checks a database schema to locate its metadata table (SCHEMA_VERSION by default). If the metadata table does not exist, it will create one
  2. It scans an application classpath for available migrations
  3. It compares migrations against the metadata table. If a version number is lower or equal to a version marked as current, it is ignored
  4. It marks any remaining migrations as pending migrations. These are sorted based on version number and are executed in order
  5. As each migration is applied, the metadata table is updated accordingly

6. Commands

Flyway supports the following basic commands to manage database migrations.

  • Info: Prints current status/version of a database schema. It prints which migrations are pending, which migrations have been applied, what is the status of applied migrations and when they were applied.
  • Migrate: Migrates a database schema to the current version. It scans the classpath for available migrations and applies pending migrations.
  • Baseline: Baselines an existing database, excluding all migrations, including baselineVersion. Baseline helps to start with Flyway in an existing database. Newer migrations can then be applied normally.
  • Convalida : convalida lo schema del database corrente rispetto alle migrazioni disponibili.
  • Riparazione : ripara la tabella dei metadati.
  • Pulisci : elimina tutti gli oggetti in uno schema configurato. Tutti gli oggetti del database vengono eliminati. Ovviamente, non dovresti mai usare clean su alcun database di produzione.

7. Conclusione

In questo articolo abbiamo mostrato come funziona Flyway e come possiamo utilizzare questo framework per rimodellare il nostro database dell'applicazione in modo affidabile.

Il codice che accompagna questo articolo è disponibile su GitHub.