In this tutorial, we’ll explore key concepts of Flyway and how we can use this framework to continuously remodel our application’s database schema reliably and easily. In addition, we’ll present an example of managing an in-memory H2 database using a Maven Flyway plugin.
Flyway updates a database from one version to the next using migrations. We can write migrations either in SQL with database-specific syntax, or in Java for advanced database transformations.
How to use Liquibase to safely and maturely evolve the database schema of your Java app.Read more →
Learn how to safely roll back migrations using Flyway.Read more →
A guide to implementing SQL and Java command-callbacks in FlywayRead more →
Migrations can either be versioned or repeatable. The former has a unique version and is applied exactly once. The latter doesn’t have a version. Instead, they are (re-)applied every time their checksum changes.
Within a single migration run, repeatable migrations are always applied last, after pending versioned migrations have been executed. Repeatable migrations are applied in order of their description. For a single migration, all statements are run within a single database transaction.
In this tutorial, we’ll mainly focus on how we use the Maven plugin to perform database migrations.
To install a Flyway Maven plugin, let’s add the following plugin definition to our pom.xml:
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>8.5.13</version>
</plugin>
The latest version of the plugin is available at Maven Central.
We can configure this Maven plugin in four different ways. In the following sections, we’ll go over each of these options.
Please refer to the documentation to get a list of all configurable properties.
We can configure the plugin directly via the <configuration> tag in the plugin definition of our pom.xml:
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>8.5.13</version>
<configuration>
<user>databaseUser</user>
<password>databasePassword</password>
<schemas>
<schema>schemaName</schema>
</schemas>
...
</configuration>
</plugin>
We can also configure the plugin by specifying configurable properties as Maven properties in our pom:
<project>
...
<properties>
<flyway.user>databaseUser</flyway.user>
<flyway.password>databasePassword</flyway.password>
<flyway.schemas>schemaName</flyway.schemas>
...
</properties>
...
</project>
Another option is to provide plugin configuration in a separate .conf file:
flyway.user=databaseUser
flyway.password=databasePassword
flyway.schemas=schemaName
...
The default configuration file name is flyway.conf and by default it loads configuration files from:
Encoding is specified by the flyway.encoding property (UTF-8 is the default one).
If we use any other name (e.g customConfig.conf) as the configuration file, then we have to specify this explicitly when invoking the Maven command:
$ mvn -Dflyway.configFiles=customConfig.conf
Finally, all configuration properties can also be specified as system properties when invoking Maven on the command line:
$ mvn -Dflyway.user=databaseUser -Dflyway.password=databasePassword
-Dflyway.schemas=schemaName
The following is an order of precedence when a configuration is specified in more than one way:
In this section, we’ll walk through the required steps to migrate a database schema to an in-memory H2 database using the Maven plugin. We use an external file to configure Flyway.
First, let’s add H2 as a dependency:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
Again, we can check the latest version of the driver available on Maven Central. We also add the Flyway plugin as explained earlier.
Next, we create myFlywayConfig.conf in $PROJECT_ROOT with the following content:
flyway.user=databaseUser
flyway.password=databasePassword
flyway.schemas=app-db
flyway.url=jdbc:h2:mem:DATABASE
flyway.locations=filesystem:db/migration
The above configuration specifies that our migration scripts are located in the db/migration directory. It connects to an in-memory H2 instance using databaseUser and databasePassword.
The application database schema is app-db.
Of course, we replace flyway.user, flyway.password, and flyway.url with our own database username, database password, and database URL, respectively.
Flyway adheres to the following naming convention for migration scripts:
<Prefix><Version>__<Description>.sql
Where:
Example: V1_1_0__my_first_migration.sql
So let’s create a directory db/migration in $PROJECT_ROOT with a migration script named V1_0__create_employee_schema.sql containing SQL instructions to create the employee table:
CREATE TABLE IF NOT EXISTS `employee` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(20),
`email` varchar(50),
`date_of_birth` timestamp
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;
Next, we invoke the following Maven command from $PROJECT_ROOT to execute database migrations:
$ mvn clean flyway:migrate -Dflyway.configFiles=myFlywayConfig.conf
This should result in our first successful migration.
The database schema should now look like this:
employee:
+----+------+-------+---------------+
| id | name | email | date_of_birth |
+----+------+-------+---------------+
We can repeat the definition and execution steps to do more migrations.
Let’s see what a second migration looks like by creating a second migration file with the name V2_0_create_department_schema.sql containing the following two queries:
CREATE TABLE IF NOT EXISTS `department` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(20)
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;
ALTER TABLE `employee` ADD `dept_id` int AFTER `email`;
Then we’ll execute a similar migration like we did the first time.
Now our database schema has changed to add a new column to employee and a new table:
employee:
+----+------+-------+---------+---------------+
| id | name | email | dept_id | date_of_birth |
+----+------+-------+---------+---------------+
department:
+----+------+
| id | name |
+----+------+
Finally, we can verify that both migrations were indeed successful by invoking the following Maven command:
$ mvn flyway:info -Dflyway.configFiles=myFlywayConfig.conf
Writing migrations manually takes a lot of time; instead, we can generate them based on our JPA entities. We can achieve this by using a plugin for IntelliJ IDEA called JPA Buddy.
To generate a differential versioned migration, simply install the plugin and call the action from the JPA Structure panel.
We simply select which source we want to compare (database or JPA entities) with which target (database or data model snapshot). Then JPA Buddy will generate the migration as shown in the animation:
Another advantage of JPA Buddy is the ability to define mappings between Java and database types. Also, it works correctly with Hibernate custom types and JPA converters.
Sometimes we may need to disable Flyway migrations under certain circumstances.
For example, it’s common practice to generate database schema based on the entities during tests. In such a situation, we can disable Flyway under the test profile.
Let’s see how easy it is in Spring Boot.
All we need to do is set the flyway.enabled property in our application-test.properties file:
flyway.enabled=false
In the more recent versions of Spring Boot, this property has been changed to spring.flyway.enabled:
spring.flyway.enabled=false
If we only want to disable automatic Flyway migration on startup, but still be able to trigger the migration manually, then using the properties described above isn’t a good choice.
That’s because in such a situation, Spring Boot will not auto-configure the Flyway bean anymore. Consequently, we’d have to provide it on our own, which isn’t very convenient.
So if this is our use case, we can leave Flyway enabled and implement an empty FlywayMigrationStrategy:
@Configuration
public class EmptyMigrationStrategyConfig {
@Bean
public FlywayMigrationStrategy flywayMigrationStrategy() {
return flyway -> {
// do nothing
};
}
}
This will effectively disable Flyway migration on application startup.
However, we’ll still be able to trigger the migration manually:
@RunWith(SpringRunner.class)
@SpringBootTest
public class ManualFlywayMigrationIntegrationTest {
@Autowired
private Flyway flyway;
@Test
public void skipAutomaticAndTriggerManualFlywayMigration() {
flyway.migrate();
}
}
To keep track of which migrations we’ve already applied and when, it adds a special bookkeeping table to our schema. This metadata table also tracks migration checksums, and whether or not the migrations were successful.
The framework performs the following steps to accommodate evolving database schemas:
Flyway supports the following basic commands to manage database migrations:
In this article, we learned how Flyway works and how we can use this framework to remodel our application database reliably.
You need to login in order to like this post: click here
YOU MIGHT ALSO LIKE