> Markdown version of [Add Flyway](https://vaadin.com/docs/v24/building-apps/forms-data/add-flyway). Section index: [llms.txt](https://vaadin.com/docs/v24/building-apps/llms.txt)

# Add Flyway

Whenever you store data in a relational database, you have to manage the database schema in some way. When the application is first installed, you have to create the entire database schema. When new features are deployed, you have to update the database schema. You might need to add new tables, introduce new columns, or move data between tables and remove the obsolete ones.

Some object-relational mapping tools, like Hibernate, can generate the initial schema for you. They may also be able to perform trivial updates to the schema, like creating new tables. In more complex cases, however, they are at a loss. For this reason, [Flyway](https://www.red-gate.com/products/flyway/community/) is the recommended tool for managing database schemas in Vaadin applications.

In this guide, you’ll learn enough about Flyway to get started using it in your Vaadin applications. At the end, a mini-tutorial helps you apply these concepts in a real Vaadin application. Because Flyway has more features than presented here, you should also read the [Flyway Documentation](https://documentation.red-gate.com/flyway).

## <a id="migrations"></a>Migrations

Flyway operates on the concept of *migrations*. A migration is a script that performs some changes on your database. Every migration is versioned. As you implement new features, you add new migrations to the project. Flyway keeps track of which migrations have been applied to the database in a separate table. This table includes a checksum of every migration script.

When Flyway runs, either at application startup, or as a part of your deployment pipeline, it compares the contents of this table with the migrations in your project. It then executes all the migrations that were missing, from the oldest to the newest version. If the database is new, Flyway creates its metadata table and executes all the migrations.

Versioned migrations should not change after they have been applied. When Flyway runs, it recalculates the checksums of all the migrations in your project, and compares them with the checksums in the table. If there are any mismatches, it throws an exception and aborts.

In addition to versioned migrations, Flyway also supports repeatable migrations. These migrations can change after they have been applied, and are automatically re-applied after every change. Repeatable migrations are always applied after the versioned migrations.

> **Important: Why you shouldn’t mix data.sql with Flyway**
>
> Spring Boot’s `data.sql` runs after Hibernate initializes the schema. When using Flyway, schema initialization is handled by Flyway itself, and using `data.sql` in parallel may lead to inconsistent state or runtime errors. Convert `data.sql` contents into migration scripts instead.

## <a id="writing-migrations"></a>Writing Migrations

You can write migrations in multiple languages, including Java, but the most common one is ordinary SQL. The migration scripts should follow a specific naming pattern. Versioned migrations start with an uppercase `V`, followed by a version number, two underscores `__`, a description, and the suffix `.sql`. For example, a migration could be named `V1__initial_schema_setup.sql`.

Repeatable migrations start with an uppercase `R`, followed by two underscores `__`, a description, and the suffix `.sql`. For example, a repeatable migration could be named `R__people_view.sql`. Repeatable migrations are sorted by their descriptions before they are applied. Take this into account if you need to apply one repeatable migration before another.

You should store your SQL scripts in the `src/main/resources/db/migration` directory of your project. If you are using a [multi-module project](https://vaadin.com/docs/v24/building-apps/deep-dives/project-structure/multi-module.md), you should store the migrations in the module that handles persistence.

For information about writing migrations in other languages than SQL, see the [Flyway Documentation](https://documentation.red-gate.com/flyway).

## <a id="migrating-on-application-startup"></a>Migrating on Application Startup

Spring Boot has built-in support for Flyway. If the `org.flywaydb:flyway-core` module is on the classpath, Flyway is automatically executed on application startup.

Flyway has built-in support for clustered environments. If you launch multiple instances of the same application, pointing to the same database, Flyway makes sure that every migration is applied only once, in the correct order.

Unless you are using an in-memory database like H2, you have to add a database specific module to the classpath, in addition to the database’s own JDBC driver. For example, use `org.flywaydb:flyway-database-postgresql` with PostgreSQL and `org.flywaydb:flyway-mysql` with MySQL.

> **Note:** See the [Flyway Documentation](https://documentation.red-gate.com/flyway/getting-started-with-flyway/system-requirements/supported-databases-and-versions) for a complete list of supported databases.

Spring Boot declares the modules in its parent POM, so you don’t have to look up their versions. To use them, add them to your project POM-file, like this:

```xml
<dependencies>
    ...
    <dependency>
        <groupId>org.flywaydb</groupId>
        <artifactId>flyway-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId> <!--(1)-->
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.flywaydb</groupId>
        <artifactId>flyway-database-postgresql</artifactId> <!--(2)-->
        <scope>runtime</scope>
    </dependency>
</dependencies>
```

1. This is the JDBC-driver for PostgreSQL.

2. This is the Flyway module for PostgreSQL.

By default, Flyway uses your application’s primary data source to apply the migrations. This means that the database user that you use to connect to the database must have enough privileges to execute Data Definition Language (DDL) statements.

From a security point of view, it is better to have one database user for DDL, and another for Data Modification Language (DML) statements. The DDL user is used by Flyway to migrate the database schema. The DML user is used by the application to query and modify data without touching the schema itself.

To configure Flyway to use a separate data source from your main application, set the `spring.flyway.url`, `spring.flyway.user`, and `spring.flyway.password` properties in your configuration. If you leave out `spring.flyway.url`, Flyway uses the same URL as the application’s primary data source.

For more information, see the [Spring Boot Documentation](https://docs.spring.io/spring-boot/how-to/data-initialization.html#howto.data-initialization.migration-tool.flyway).

> **Caution: Don’t defer data source initialization**
>
> When you are using Flyway, you have to make sure the `spring.jpa.defer-datasource-initialization` configuration property is `false`. Otherwise, **your application fails to start**. The purpose of having this flag set to `true` is to allow JPA to create the schema before populating the database with data from `data.sql`. However, now Flyway is taking care of both of these tasks.

## <a id="migrating-with-maven"></a>Migrating with Maven

Sometimes, you may want to run the Flyway migrations as a separate build step. For example, you may not want to make the DDL user credentials available to the application itself for security reasons. Flyway has a Maven plugin that allows you to run the migration scripts as a part of your build chain.

To run Flyway with Maven, you should still keep the migration scripts in the same directory as you did when running Flyway at application startup. However, you should not add any Flyway dependencies to your project. Instead, you should add the Flyway plugin, like this:

```xml
<build>
    <plugins>
        <plugin>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-maven-plugin</artifactId>
            <dependencies>
                <dependency>
                    <groupId>org.flywaydb</groupId>
                    <artifactId>flyway-database-postgresql</artifactId>
                    <version>${flyway.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.postgresql</groupId>
                    <artifactId>postgresql</artifactId>
                    <version>${postgresql.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
```

Note: When adding dependencies inside a Maven plugin declaration, you must explicitly specify their versions-—even if those versions are declared in a parent POM. You can reference Spring Boot’s property values like `${flyway.version}` to avoid hardcoding them.

Now, whenever you want to run Flyway, execute the following command:

```terminal
$ mvn -Dflyway.user=YOUR_DDL_USER -Dflyway.password=YOUR_DDL_USER_PASSWORD -Dflyway.url=YOUR_DB_URL flyway:migrate
```

For more information about what you can do with the Flyway Maven plugin and how to configure it, see the [Flyway Documentation](https://documentation.red-gate.com/flyway/reference/usage/maven-goal).

## <a id="try-it"></a>Try It

In this tutorial, you’ll add Flyway to a real Vaadin application.

Set Up the Project

First, generate a [walking skeleton](https://vaadin.com/docs/v24/getting-started/start.md) with either a Flow or a Hilla UI, and [open](https://vaadin.com/docs/v24/getting-started/import.md) it in your IDE.

Add Maven Dependencies

Open `pom.xml` and add the following dependencies:

pom.xml

```xml
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>
```

Write Migration Script

Create a new directory `src/main/resources/db/migration`. In this directory, create a file `V1__task_management.sql`:

V1\_\_task\_management.sql

```sql
create sequence task_seq increment by 50;

create table task
(
    task_id       bigint                   not null,
    description   varchar(300)             not null,
    creation_date timestamp with time zone not null,
    due_date      date,
    primary key (task_id)
);
```

Open `[application package].examplefeature.Task` and compare the `@Entity` and `@Column` annotations with the SQL script. The names, size, and nullability should match.

Disable Hibernate’s Automatic DDL Generation

Open `src/main/resources/application.properties` and remove the line that sets `spring.jpa.hibernate.ddl-auto` to `update`.

Run the Integration Test

Run the `TaskServiceTest` integration test in your IDE. The test should pass, and in the log, you should see messages like the following:

```
Schema history table "PUBLIC"."flyway_schema_history" does not exist yet
Successfully validated 1 migration
Creating Schema History table "PUBLIC"."flyway_schema_history" ...
Current version of schema "PUBLIC": << Empty Schema >>
Migrating schema "PUBLIC" to version "1 - task management"
Successfully applied 1 migration to schema "PUBLIC", now at version v1
```

Run the Application

Now [run](https://vaadin.com/docs/v24/getting-started/run.md) the application. You should see similar messages in the startup log. Open the application in your browser and create some tasks. Everything should work normally.

Final Thoughts

You’ve now implemented Flyway for schema management in a Vaadin application and replaced Hibernate’s DDL auto-generation with a structured, version-controlled approach.
