Codersee
Kotlin on the backend
Codersee
Kotlin on the backend
In this tutorial, we will learn what exactly Flyway is and how we can configure it when working with Spring Boot.
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:
To put it simply, Flyway is a database versioning tool. Just like Git is a version control system for our codebase, Flyway takes care of database schema management.
Let’s imagine that we’ve started a Spring Boot project, which communicates with the 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:
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.
[elementor-template id=”9007393″]
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.
As the first step, let’s add the necessary imports:
implementation("org.flywaydb:flyway-core:10.13.0") runtimeOnly("org.flywaydb:flyway-database-postgresql:10.13.0") implementation("org.postgresql:postgresql:42.7.3") implementation("org.springframework.boot:spring-boot-starter-data-jdbc") //alternatively, we could use spring-boot-starter-data-jpa
The above 4 are the bare minimum required to work with Flyway and Spring Boot.
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).
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.
For now, let’s check if our setup is working correctly (please make sure that you have a database with an empty schema up and running).
After we run the application, we should see the following in the 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.
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.
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:
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.
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
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 migration 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
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.
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 to extend 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 its 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 class 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).
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! 🙂