It is important to develop a well-designed schema at the beginning of the project, but with the new requirements, changing your initial schema is hard to avoid. These changes must be carefully applied to avoid compromising existing data but also to meet new expectations. Migration group changes that modify database structure. It allows controlled transition from their current state to a new desired state (e.g., adding, deleting columns or tables).

One of the commonly used open-source database migrations tools is Flyway. It updates a database from one version to the next using migrations. For a single migration, all statements are run within a single database transaction. Migrations can be written in SQL or Java for advanced database transformations. Flyway’s SQL-script based migrations are good enough for most use cases. Java-based migrations provide an easy and powerful way to implement requested logic to adapt existing data in case SQL script cannot be used. Flyway automatically finds and executes SQL scripts as well as migration classes.

CREATE TABLE demo_user (
    name VARCHAR(100) NOT NULL

Simple SQL migration V1__init.sql

Java-based migrations must implement the JavaMigration interface. However, it is easier to extend BaseJavaMigration abstract class as it encourages Flyway’s default naming convention, enabling Flyway to automatically extract the version and the description from the class name. It only remains to implement the interface. Class name should follow Flyway’s naming schema <Prefix><Version>__<Description>.java. When migrations are being run, Flyway detects the current database version, scans for all SQL and Java migrations, and executes the required ones.

Mentioned naming schema parts have different interpretations:

  • Prefix – V for versioned migrations, U for undo migrations, R for repeatable migrations
  • Version – Migration version number. Major and minor versions may be separated by an underscore. Underscores are automatically replaced by dots at runtime.
  • Description – Textual description of the migration. A double underscore separates the description from the version numbers. Single underscores (automatically replaced by spaces at runtime) separate the words.
public class V2__trigger_java_migration extends BaseJavaMigration {

    public void migrate(Context context) {
        System.out.println("Triggering V2 migration...");

Simple Java-based migration

As part of the migration run, Flyway performs change detection validation using a migration checksum. Java-based migrations, by default, do not have a checksum, so to be considered for the mentioned validation, getChecksum()method must be implemented.

When using Flyway in combination with Spring Boot and Hibernate, auto-configuration ensures that database migrations have run before Hibernate is initialized. That is, one should not rely on Flyway auto-configuration and use Flyway to populate tables created by Hibernate. The solution is to use Flyway to both create and populate tables, along with switching off Hibernate’s table creation: spring.jpa.hibernate.ddl-auto=none

Another solution is to disable Flyway auto-configuration: flyway.enabled=false With this, it is possible to adjust the configuration to support the requested changes. Flyway initialization time can be changed, and one useful use case is depending on Hibernate. Another use case that may come in handy is using Dependency Injection and already defined application beans, running Java migrations after application startup. This approach requires defining component with custom Flyway configuration that will pick up both SQL and Java migrations.

public class FlywayConfiguration implements CommandLineRunner {

    private JavaMigration[] javaMigrations;
    private DataSource dataSource;

    public FlywayConfiguration(DataSource dataSource) {
        this.dataSource = dataSource;

    public void setJavaMigrations(JavaMigration[] javaMigrations) {
        this.javaMigrations = javaMigrations;

    public void run(String... args) {
        Flyway flyway = Flyway.configure()


Flyway custom configuration

After configuring Flyway initialization time, Java migration class can use service methods, etc. Note: this feature is available with importing flyway-core dependency version 6.0.0 and higher.

public class V3__insert_demo_user extends BaseJavaMigration {
    private final DemoUserService demoUserService;

    public V3__insert_demo_user(DemoUserService demoUserService) {
        this.demoUserService = demoUserService;

    public void migrate(Context context) throws Exception {
        demoUserService.createDemoUser("John Doe");

Java-based migration using Dependency Injection

Flyway keeps track of all created migrations, marking them as pending, successful, or failed. All this information (and much more) provides an automatically created flyway_schema_history table. This way, all database versioning, and made changes are being monitored in one place. 

version description type checksum execution time success
1 init SQL -899440118 7 true
2 trigger java migration JDBC [null] 2 true
3 insert demo user JDBC [null] 92 true

Relevant information about executed migrations

Interested in more?

Click here for Atlantbh Blogs and Success Stories about Software Development.

Software DevelopmentTech Bites
February 23, 2024

Background Jobs in Elixir – Oban

When and why do we need background jobs? Nowadays, background job processing is indispensable in the world of web development. The need for background jobs stems from the fact that synchronous execution of time-consuming and resource-intensive tasks would heavily impact an application's  performance and user experience.  Even though Elixir is…

Want to discuss this in relation to your project? Get in touch:

Leave a Reply