Database Migrations in Spring Boot with Flyway and Neon
Learn how to manage database schema changes in a Spring Boot application using Flyway with Neon Postgres.
Database schema management is an essential part of every application development and maintenance process.
As your application grows, you need a reliable way to manage database changes across different environments.
This guide will walk you through setting up and using Flyway for database migrations in a Spring Boot application with Neon Postgres.
Prerequisites
Before we begin, ensure you have:
- Java Development Kit installed
- Maven for dependency management
- A Neon account for serverless Postgres
- Basic familiarity with Spring Boot and SQL
Instead of Maven, you can use Gradle for dependency management. The steps will be similar but for this guide, we'll use Maven.
Setting up the Project
-
Let's create a new Spring Boot project using Spring Initializr with the following dependencies:
- Spring Web
- Spring Data JPA
- PostgreSQL Driver
- Flyway Migration
Once you've selected the dependencies, click "Generate" to download the project. Then, extract the ZIP file and open it in your favorite IDE.
-
If you're using Maven, your
pom.xml
should include these dependencies:
Configuring the Database Connection
Now that we have our project set up, let's configure the database connection.
To configure your Neon database connection details, open the application.properties
file in src/main/resources
and add the following properties:
Replace the placeholders with your actual Neon database credentials.
Note that we set spring.jpa.hibernate.ddl-auto=validate
to prevent Hibernate from automatically modifying the schema. Flyway will handle all schema changes.
To learn more about managing your database schema using Hibernate, refer to the Database Schema Changes with Hibernate, Spring Boot, and Neon guide.
Creating Migration Scripts
Flyway uses SQL scripts for migrations. These scripts should be placed in the src/main/resources/db/migration
directory.
Unlike other migration tools, Flyway uses a version-based naming convention for migration scripts so that it can track the order in which they should be applied. This ensures that migrations are applied in the correct order and only once, but you need to be careful when renaming or modifying existing scripts.
Naming convention for migration scripts:
V<VERSION>__<DESCRIPTION>.sql
- Example:
V2__Create_users_table.sql
We will start with V2__
as the first migration script, as Flyway will use V1__
for its internal schema history table.
Let's create our first migration script:
-
Create a file named
V2__Create_users_table.sql
insrc/main/resources/db/migration
: -
Create another file named
V3__Create_posts_table.sql
:
These scripts will create the users
and posts
tables in your Neon database when you run the migrations.
Running Migrations
Now that we've configured Flyway with our Spring Boot application and Neon database, we can proceed to run the database migrations.
There are two primary methods to execute Flyway migrations: using the Flyway Maven plugin or programmatically through the Flyway API. Let's explore both approaches in detail.
1. Using the Flyway Maven Plugin
The Flyway Maven plugin allows you to run migrations directly from the command line, which can be useful for CI/CD pipelines or local development.
First, add the Flyway Maven plugin to your pom.xml
file:
Next, run the following command to create the schema history table in your database:
This will create the flyway_schema_history
table if it doesn't already exist. The table is used by Flyway to track the applied migrations.
Now, you can run the following command to apply pending migrations:
This command will execute all pending migrations in the order defined by their version numbers.
Additional useful Flyway Maven plugin commands include:
mvn flyway:info
: Displays the status of all migrations. This includes the version, description, type, and state of each migration.mvn flyway:validate
: Validates the applied migrations against the available ones. This ensures that the schema history table is correct and that all migrations were applied successfully.mvn flyway:repair
: Repairs the schema history table. This command is useful if you manually modify the schema history table or if a migration fails.
2. Using the Flyway API Programmatically
For more fine-grained control or to integrate migration execution within your application lifecycle, you can use the Flyway API programmatically.
Start by creating a configuration class to set up the Flyway bean:
This configuration automatically triggers the migration when the application starts. You can now run your Spring Boot application to apply the migrations:
Alternatively, if you want more control over when migrations run, you can remove the initMethod = "migrate"
and call the migrate()
method manually:
You can then inject this service and call the migrateDatabase()
method when appropriate, such as during application startup or as part of a maintenance routine.
Handling Schema Changes
As your application evolves, you'll need to make changes to your database schema. Here's how to handle common scenarios:
Adding a New Column
To add a new column to an existing table, you will just need to create a new migration script with the ALTER TABLE
statement.
Create a new migration script, e.g., V4__Add_user_role.sql
:
After adding the new migration script, you can run the migration using the Flyway Maven plugin or programmatically by starting the Spring Boot application depending on your preferred method.
Modifying an Existing Column
To modify an existing column, you can create a new migration script with the ALTER TABLE
statement, e.g., V5__Modify_user_role.sql
:
After adding the new migration script, you can check the status of your migrations using the Flyway Maven plugin:
You should see the new migration in the list with a Pending
state indicating that it hasn't been applied yet:
Then run the migration using your preferred method to apply the changes.
Creating a New Table
To create a new table, you would just add a new migration script with the CREATE TABLE
statement, e.g., V6__Create_comments_table.sql
:
Handling Rollbacks
The Flyway Community Edition doesn't support automatic rollbacks. When you need to roll back a migration, you can create a new migration script to undo the changes. This script should be named with a higher version number than the original migration.
Create a file named V7__Remove_user_role.sql
:
Then run the migration as usual to apply the rollback. This will remove the role
column from the users
table as defined in the script.
The Flyway Pro and Enterprise Editions offer additional features like undo
and repair
commands for automatic rollback and fixing failed migrations. You can explore these options if you require more advanced rollback capabilities.
Best Practices
There are several things to keep in mind when managing database migrations:
-
Always keep your migration scripts in version control along with your application code.
-
Make sure that your migrations can be applied multiple times without changing the result beyond the initial application.
-
When possible, write migrations that are backward compatible with the previous version of your application. This will make it easier to roll back changes if needed.
-
Test your migrations thoroughly in a non-production environment before applying them to production. A great way to do this is by using the Neon branching feature to create a separate environment for testing with your production data without affecting the live environment.
-
Once a migration has been applied to any environment, avoid modifying it. Instead, create a new migration to make further changes.
Conclusion
Using Flyway with Spring Boot and Neon Postgres provides a production ready solution for managing database schema changes. By following these practices, you can ensure that your database schema evolves safely and consistently across all environments.
Remember to always test your migrations thoroughly and have a solid backup and rollback strategy in place. Neon's features like branching and point-in-time recovery can be a great addition to your already existing lifecycle of your database schema.