Skip to main content

Introduction of Flyway with Spring Boot

· 5 min read
Guster
Full-stack Staff Engineer

image

Flyway is the Apache v2 licensed open-source tool that makes database migrations easy. You can think of Flyway as version control for your database. It lets you evolve your database schema easily and reliably across all your instances.

Some of its features included:

  • Smooth database migration
  • Version control
  • Rollback machanism
  • Callbacks (Java callback and SQL callback)

1. Installation

Add the Flyway dependency to your pom.xml or build.gradle if you're using Gradle instead of Maven.

Maven:

<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>6.4.3</version>
</dependency>

Gradle:

implementation 'org.flywaydb:flyway-core:6.4.3

Optionally, if you want to use Flyway with command line eg. mvn flyway:info / ./gradlew flywayInfo, add the following plugin as well.

Maven:

<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>6.4.3</version>
</plugin>

Gradle:

plugins {
id "org.flywaydb.flyway" version "6.4.3"
}

Here are some of the common commands you could use. Also see here for the full list.

MavenGradleDescription
migrateflywayMigrateMigrates the database
cleanflywayCleanDrops all objects in the configured schemas
infoflywayInfoPrints the details and status information about all the migrations

2. Configuration

Flyway works out of the box if you integrate it from the beginning of the project. However, if you plan to integrate to the existing database you can do so as well, just require a little configuration. See Section 2.1

2.1 Enable baseline-on-migration

Note: For existing database only

In your application.yml, set baseline-on-migration to true

spring:
flyway:
enabled: true
baseline-on-migrate: true

Note that you only need to set this the first time. Subsequent migration you can choose to turn it off.

Also, for existing database, you cannot start the Flyway migration version from V1. Name your migration from anything higher than V1 onwards, such as V1_0_1

2.2 Create a migration file

Flyway reads the migration script files by default from your src/main/resources/db/migration directory.

── db
   └── migration
   └── V1_0_0__create_users.sql

The migration file has its own naming convention that MUST BE STRICTLY FOLLOWED.

The migration file format = <Prefix><Version>__<Description>.sql where prefix is V typically followed by version number which is separated by underscore _ or a dot .. After double underscores __ is the description of your choice also separated words by underscore.

Inside the migration file contains the pure SQL statements. For example:

CREATE TABLE IF NOT EXISTS `users` (
id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(255),
password VARCHAR(255),
email VARCHAR(255),
role VARCHAR(255),
PRIMARY KEY (id)
);

After running the application, you should see something like this in the console.


3 Callbacks

Flyway emits certain events during the migration process that you can hook into. Refer here for all possible callback events. Practically, what you usually need is afterEachMigrate event, which runs after each successful migration.

There are 2 types of Callback: SQL Callback and Java Callback

3.1 SQL Callback

This is the simplest form of callback you could use. All you have to do is to create a .sql file of certain naming convention. For example: beforeMigrate.sql, afterMigrate.sql, and Flyway will automatically execute it during the migration process.

By default, Flyway will search for these files in the src/main/resources/db/migration directory. You can however customize the location with Flyway's properties. Let's do this.

3.1.1 Define the callback location in application.yml

spring:
flyway:
locations:
- classpath:db/migration
- classpath:db/callback # for sql callback scripts

3.1.2 Create a directory db/callback for callback scripts

── db
   └── migration
   └── callback
   └── beforeMigrate.sql
   └── afterMigrate.sql
-- Example of afterMigrate.sql
SELECT * FROM `users`

After running, you will see the callback output in the console:

You can refer here for the full list of callback events.

3.2 Java Callback

Java callback is the most flexible way to perform custom business logic after migration. You can even have different callback based on a specific migration version.

Example:

@Component
public class MyFlywayCallback implements Callback {

private UserRepository userRepository;

public MyFlywayCallback(@Lazy UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public boolean supports(Event event, Context context) {
// telling Flyway to only trigger callback for these events
return event.equals(Event.BEFORE_EACH_MIGRATE) || event.equals(Event.AFTER_EACH_MIGRATE);
}

@Override
public boolean canHandleInTransaction(Event event, Context context) {
return false;
}

@Override
public void handle(Event event, Context context) {
switch (event) {
case BEFORE_EACH_MIGRATE:
// Some logic before the migration begins...
break;
case AFTER_EACH_MIGRATE: {
MigrationInfo migration = context.getMigrationInfo();
String version = migration.getVersion().getVersion();
Faker faker = new Faker();

switch (version) {
case "1.0.0": {
// v1.0.0 migration callback logic
break;
}
case "1.0.1": {
// v1.0.1 migration callback logic
break;
}
}
}
}
}
}

It's helpful to note that Flyway will execute Java Callback first before SQL Callback.


4. Migration History

Flyway keeps track of the migrations in its own table flyway_schema_history. This provides you the overview of the migration history and the status of each migration.


Conclusion

This is just tip of the Flyway iceberg. If you are interested in the more advanced features, feel free to take a look at the official documentation.

Have fun!

Check out the demo project on Github.