How to configure Flyway database migrations in a SpringBoot project
Gabi
Posted on March 19, 2019
Hello everyone, in this article I am going to talk about my own latest experience with a REST API project implemented with Spring Boot, making use of Flyway for database migrations and dealing with all the needed configurations.
Project stack: Java 10, Maven, Spring Boot 2.1.2, Spring Security, PostgreSql 42.2.5, Flyway 5.2.4, React, Redux, Bootstrap, JUnit 4.12, Model Mapper, Lombok.
As part of the project, I implemented a REST API project using Spring Boot, on the client side I have a React web application and I have chosen PostgreSql for database and Flyway to help initialize, migrate and populate a new database instance.
Flyway is great because it helps avoiding a lot of human error when dealing with database scripts and versions, by automatically dealing with this part of the project. This takes away that cumbersome task of remembering the team's name conventions and manually deploying.
Another reason to use Flyway is because it keeps a list of all the scripts that have ran with success or not in the automatically created table: flyway_schema_history. This is a plug-and-play feature that acts as a version control for database migrations. Everything is bundled together into one single transaction by default and this works great for my project.
1.Spring Boot configurations
First you need to create the application profiles. For my project I have created three type of profiles:
TEST - to run integration tests
DEV - actual implementation stage
PROD - what is stable and pushed to production
Configure the profiles in pom.xml as well:
2. Flyway configurations
You need to configure the Flyway-Maven plugin in the pom.xml as shown bellow:
Flyway properties and scripts reside in the "src/main/resources" folder of the application. Here you can add as many configuration details as you need.
Each profile has it's own properties file and package using this name pattern: "db/{name_here}"
Example configuration for application- dev.properties:
Let's dive into more details:
"db/migration" - contains all the versions for every compilation. Flyway explains here the name pattern that you must use: V*_ *.sql
Example: V1 _InitDatabase.sql
The last number version script from here is run when the application starts. If the script was already run, don't worry it will not execute again. If you look in the stacktrace while the application is compiled and started, you can spot that this step happens after below step:
Initializing Spring embedded WebApplicationContext
In case the version is old (has been run already) nothing happens.
Schema "public" is up to date. No migration necessary.
"test/db/migration" - Here you can add all the database migration scripts that will run or will be skipped but only on profile TEST. Here you can add data that you need for end-to-end tests.
"dev/db/migration" - Here you can add all the database migration scripts that will run or will be skipped but only on profile DEV. I chose to add some insert SQLs to populate my database from the get-go.
"prod/db/migration" - Here you can add all the database migration scripts that will run or will be skipped but only on profile PROD (production).
The important take away from the above is that if you keep your databases separate you can control your data and not have test data in your development database and of course that Production database is kept clean.
Configure the plugin for Flyway in the pom.xml as well:
3. Integration tests run on Test profile only
Testing is important. I remind myself this a lot these days. Integration tests in Spring are easy and fast. Below is an example test for login, with real code just modified paths.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = WeddingManagementApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class LoginControllerTest {
@Autowired
private TestRestTemplate restTemplate;
@LocalServerPort
private int port;
private String getRootUrl() {
return "http://localhost:" + port + "/api/v1";
}
@Test
public void testLogin() throws Exception {
User user = new User("gabriela", "admisnadmin", "admin@gmail.com", Role.ADMIN);
ResponseEntity<User> postResponse = restTemplate.postForEntity(getRootUrl() + "/login", user, User.class);
assertNotNull(postResponse);
assertNotNull(postResponse.getBody());
}
}
This will run on TEST profile and the "development profile" is left untouched. Great. If you are running persistence layer tests, it would be great to clean the database after but I haven't seen this yet in a project, maybe because it's difficult to maintain?
Callbacks in Flyway can be very useful. There are a lot of options to use and at this point in my project I used only one callback afterMigrate, but check out their official document for way more details.
This is how I am using Flyway at this point and I love it. If you know more tricks and hints to make this better please leave a comment below so we all learn.
If you want to read more stuff, check out my blog here: https://gabriela.codes
Posted on March 19, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.