Flyway Migrations With Spring Boot

Credits

1. Introduction

In this blog post, I would like to show you what exactly Flyway is and how to configure it, when working with Spring Boot.

Together, we will spend some time to figure out:

  • why do we even need it in our project?
  • how can we quickly configure Flyway with Spring Boot?
  • finally, how can we customize its behavior?

2. What is Flyway and What Problems Does It Solve?

To put it simply, Flyway is a database versioning tool. Just like Git is a version control system for our codebase, Flyway takes care about database schema management.

Let’s imagine that we’ve started a Spring Boot project, which communicates with PostgreSQL database. We’ve been working on our own, implementing new features, adding new tables and changing the schema many times. At some point, a new programmer joins our team, so we simply export our local or remote database and send him the script, so that he will be able to set up a local environment.

So far so good, but what will happen when more people join our team? Or we would like to create more testing environments? Of course, we could save the script and update it each time, we change anything, but this can introduce plenty of problems, for example:

  • when multiple people are editing schema asynchronously, how can we make sure that all their scripts were applied in a specific environment?
  • how can we be sure that all developers have exactly the same (or a specific) version on their local?
  • how can we determine the correct order of scripts, so that nothing breaks when applying them?

If you are not convinced after reading the above points, then imagine working without Git when cooperating with others and exchanging written code manually 🙂 Technically- doable- nevertheless definitely not recommended.

Of course, Flyway is not the only tool we could use with Spring Boot, however, it is definitely easy to work with.

3. Setup Flyway With Spring Boot

With all of that being said, let’s switch to the practice part. If you would like to simply download the skeleton for this tutorial, I’ve uploaded it to this GitHub repository. Nevertheless, I highly encourage you to set up the Spring Boot project on your own and follow below steps. This way, you’re definitely gonna learn more.

3.1. Imports

As the first step, let’s add necessary imports:

implementation("org.flywaydb:flyway-core:8.5.11")
implementation("org.springframework.boot:spring-boot-starter-data-jdbc") //alternatively, we could use spring-boot-starter-data-jpa

The above two are the bare minimum required to work with Flyway and Spring Boot.

3.2. Edit Application Properties

As the next step, we have to make sure that we’ve set up the database connection properly.

The easiest way to do so is via application.yaml (or application.properties) file:

spring:
  datasource:
    url: "jdbc:postgresql://localhost:5432/your-db-name"
    username: db-username
    password: db-password

By default, Flyway will use the @Primary Data Source (which we’ve configured above).

3.3. Add Migrations Directory

Nextly, we have to add /db/migration directory to the /resources. If we don’t do so and try to run the application, it will fail with the following error:

Flyway failed to initialize: none of the following migration scripts locations could be found:

classpath:db/migration

Of course, we can customize that, and we will learn how to do that later.

3.4. Validation

For now, let’s check if our setup if working correctly (please make sure that you have database with empty schema up and running).

After we run the application, we should see the following in logs:

Flyway Community Edition 8.5.11 by Redgate
...
Successfully validated 0 migrations (execution time 00:00.009s)
No migrations found. Are your locations set up correctly?
Creating Schema History table "public"."flyway_schema_history" ...
Schema "public" is up to date. No migration necessary.

Successfully validated 0 migrations (execution time 00:00.009s)

No migrations found. Are your locations set up correctly?

Creating Schema History table “public”.”flyway_schema_history” …

Schema “public” is up to date. No migration necessary.

Moreover, we can see that a new table called flyway_schema_history was added to our schema.

In brief, Flyway uses this table to manage versions and control applied, modified or added scripts. If you would like to see more theory, then this article from their documentation is a great source for that.

3.5. Add Migration

Finally, let’s add our first migration file called V1__Create_User_Table.sql. This “strange” filename is the default Flyway naming convention, which consists of:

  • a prefix– B for baseline, R for repeatable,  U for undo and V for versioned migrations
  • migration number– number 1 in our case. If we would like it to be 1.1, then we should start the name with V1_1
  • double underscore– separates version from migration description
  • description– underscores are translated to spaces, so our migration will become Create User Table

With that being said, let’s add an example SQL script:

CREATE TABLE IF NOT EXISTS app_user (
  id SERIAL NOT NULL PRIMARY KEY,
  name TEXT NOT NULL
);

After that, let’s run our Spring Boot application once again and check out printed logs:

Migrating schema "public" to version "1 - Create User Table"

As can be seen, migration was applied successfully and a new table was added to our database.

4. Flyway Customization With application.properties

4.1. Enable/Disable Flyway

By default, Flyway is enabled when added to Spring Boot. Nevertheless, if we would like to change that, we can set the enabled flag:

spring:
  flyway:
    enabled: false

4.2. Set Custom Migrations Directory

As I’ve mentioned earlier, the default directory is /db/migration, but it can be customized with spring.flyway.locations property:

spring:
  flyway:
    locations: "classpath:my-custom-dir,classpath:my-custom-dir-2"

As we can see, we can provide multiple directories. When working with Spring Boot, a classpath means resources directory. Alternatively, we can refer to any directory in the system using filesystem instead.

It’s worth mentioning that when we specify multiple directories, then at least one of them has to exist (but not all of them). Moreover, we can’t duplicate migrations versions among our directories.

So, in practice:

/resources
    /my-custom-dir
        V2__one.sql
    /my-custom-dir-2
        V1__one.sql

These two directories are treated, just like one and if we would specify V1 version twice, then the FlywayException would be thrown:

Found more than one migration with version 1

4.3. Vendor Aware Directory

Additionally, Flyway allows us not only to specify hard-coded paths, but we can use a vendor placeholder, which will be resolved on the fly:

spring:
  flyway:
    locations: "classpath:my-custom-dir/{vendor}"

With the above config, Flyway will look for migrations files inside the /resources/my-custom-dir/postgresql directory. You can find a full list of supported vendors right here.

5. Flyway Java (Kotlin) Configuration

It’s worth mentioning that Flyway allows us to apply migrations written in Java. When working with Spring Boot, they will be auto-configured with any bean implementing a JavaMigration interface. However, this interface requires us to implement a few methods, so the recommended way is extending the BaseJavaMigration abstract class instead (and sufficient in most cases).

Given that, let’s have a look at the example written in Kotlin (which technically does not differ too much from Java counterpart):

@Component
class V1__Create_User_Table : BaseJavaMigration() {
    override fun migrate(context: Context) {
        val update = context.connection.createStatement()
        update.execute(
            """
            CREATE TABLE IF NOT EXISTS app_user (
                id SERIAL NOT NULL PRIMARY KEY, 
                name TEXT NOT NULL
            );
            """.trimIndent()
        )
    }
}

@Component
class V2__Drop_User_Table : BaseJavaMigration() {
    override fun migrate(context: Context) {
        val update = context.connection.createStatement()
        update.execute("DROP TABLE app_user;")
    }
}

As we can see, the only thing we have to do is to override the migrate method and that classes names should follow the default Flyway naming convention.

If you are wondering whether we can mix SQL files with Spring Beans configuration, then the answer is yes (however we should be really cautious about it).

6. Flyway With Spring Boot Summary

And that would be all for this tutorial about Flyway With Spring Boot. I believe the knowledge presented here will be sufficient to easily incorporate this tool into any Spring Boot project. Moreover, if you’d like to learn how to incorporate Flyway to an existing Spring Boot project, or how to use it with Spring WebFlux, then check out the flyway tag.

Let me know what you think about database version control tools. Do you like Flyway, or maybe you prefer some other tool?

Have a great day and see you in the next articles! 🙂

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments