
How to perform database schema versioning with Flyway and Spring?
Natan Ferreira
- 0
- 121
Flyway is a tool used for database migration control. Migrations are executed automatically, and we also have a history of database modifications.
Flyway supports many databases, but in this example, we will use MySQL.
Requirements
- JDK 17;
- Spring 3;
- SpringDataJpa;
- Mysql;
- Flyway;
Project
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
With this site, you can create your Spring project: https://start.spring.io
To establish a connection with the database, we need to specify some variables in the properties file:
spring.datasource.url=jdbc:mysql://localhost/company?createDatabaseIfNotExist=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=
I am using MySQL through a Docker container, with the root user and no password.
I created two entities that will represent two tables in the database, and the mapping was done using annotations.
package natan.code.company.domain.model;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@Entity
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Branch {
@EqualsAndHashCode.Include
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
}
package natan.code.company.domain.model;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.OffsetDateTime;
@Data
@Entity
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@EqualsAndHashCode.Include
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String email;
@Column(nullable = false, columnDefinition = "datetime", name = "birth_datetime")
private OffsetDateTime dateOfBirth;
@ManyToOne
@JoinColumn(name = "branch_id", nullable = false)
private Branch branch;
}
Let’s observe what happens when the project starts.

There are no migrations, and a table called “flyway_schema_history” was created in our database. What does it do?

Its purpose is to store the version history. When a migration is executed, it is stored in this table. This is how Flyway keeps track of the history to manage it.
And where is the migration?
Inside the resources
folder, we need to create two more folders: db/migration
.
Inside the migration
folder, we can create the migration file. We need to follow this structure: “V001__create_initial_tables.sql”.

We can also have version variations, such as 1.1, 1.2, 2.1.
We will use this pattern: 001, 002, 003, and so on.
In the migration, we will specify the following script:
CREATE TABLE `branch` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `employee` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`birth_datetime` datetime(6) DEFAULT NULL,
`branch_id` bigint NOT NULL,
PRIMARY KEY (`id`),
KEY `FK1ppr8greedyrey8nchpr0v4dn` (`branch_id`),
CONSTRAINT `FK1ppr8greedyrey8nchpr0v4dn` FOREIGN KEY (`branch_id`) REFERENCES `branch` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
When running the application, we get the following result:

The migration was executed, and we can see in the “flyway_schema_history” table the history of migrations.


Now we need to add the cellphone
field to the “employee” table.
I created the migration “V002__adding_cellphone_in_employee.sql” with the following script.
alter table `employee` add cellphone varchar(15);
Upon execution, we get the following result:



Author
-
I am a seasoned Full Stack Software Developer with 8+ years of experience, including 6+ years specializing in Java with Spring and Quarkus. My core expertise lies in developing robust RESTful APIs integrated with Cosmos Db, MySQL, and cloud platforms like Azure and AWS. I have extensive experience designing and implementing microservices architectures, ensuring performance and reliability for high-traffic systems. In addition to backend development, I have experience with Angular to build user-friendly interfaces, leveraging my postgraduate degree in frontend web development to deliver seamless and responsive user experiences. My dedication to clean and secure code led me to present best practices to my company and clients, using tools like Sonar to ensure code quality and security. I am a critical thinker, problem solver, and team player, thriving in collaborative environments while tackling complex challenges. Beyond development, I share knowledge through my blog, NatanCode, where I write about Java, Spring, Quarkus, databases, and frontend development. My passion for learning and delivering innovative solutions drives me to excel in every project I undertake.