DEV Community

Thomas
Thomas

Posted on • Originally published at bootify.io

Loading initial data in Spring Boot

We have already initialized the database schema using Hibernate, Flyway or Liquibase. However, in addition to the schema, we often require certain data that is necessary for the application during runtime. This could be e.g. a table Country, in which the ISO code and a currency is stored. What options are available for this in a Spring Boot application?

Using an ApplicationRunner

An ApplicationRunner is executed immediately after the start of our Spring Boot application. If there are multiple ApplicationRunners in the application, they can also be sorted in the desired order using @Order.

@Component
public class CountryLoader implements ApplicationRunner {

    private final CountryRepository countryRepository;

    @Autowired
    public CountryLoader(final CountryRepository countryRepository) {
        this.countryRepository = countryRepository;
    }

    public void run(final ApplicationArguments args) {
        if (countryRepository.count() != 0) {
            return;
        }
        final Country germany = new Country();
        germany.setCode("DE");
        germany.setCurrency("EUR");
        countryRepository.save(germany);
        final Country states = new Country();
        states.setCode("US");
        states.setCurrency("USD");
        countryRepository.save(states);
    }

}
Enter fullscreen mode Exit fullscreen mode

  Using an ApplicationRunner to initialize data

This option provides the highest flexibility, as the process can be controlled directly in our application.

Using the data.sql

If we are working with a relational database, we could simply place a data.sql in our resources folder. This script will be automatically executed by Spring Boot against the configured DataSource during startup.

INSERT INTO country (code, currency) VALUES ('DE', 'EUR') ON CONFLICT DO NOTHING;
INSERT INTO country (code, currency) VALUES ('US', 'USD') ON CONFLICT DO NOTHING;
Enter fullscreen mode Exit fullscreen mode

  Special insert script for PostgreSQL

We have to ensure that the values are not created multiple times. If our database schema is created by Hibernate, we should also add the following property to run our script only after Hibernate made its changes.

spring:
  jpa:
    defer-datasource-initialization: true
Enter fullscreen mode Exit fullscreen mode

  Running the data.sql after Hibernate

Using changelogs

If we have chosen Flyway or Liquibase for schema generation, we can also use them for loading our initial data. They implicitly ensure that the changes are executed exactly once against the connected database.

In case of Liquibase we simply add another changelog with a higher timestamp into our changelogs folder.

databaseChangeLog:
  - changeSet:
      id: countries-initial
      author: bootify.io
      changes:
        - insert:
            tableName: country
            columns:
            - column:
                name: code
                value: DE
            - column:
                name: currency
                value: EUR
        - insert:
            tableName: country
            columns:
            - column:
                name: code
                value: US
            - column:
                name: currency
                stringValue: USD
Enter fullscreen mode Exit fullscreen mode

  Liquibase changelog in resources/changelogs/2023-01-18_11-00.yml

In case of Flyway we create our migration script directly in the dialect of the used database. We store it in resources/db/migration/V002__INITIAL_COUNTRIES.sql so that it is executed immediately after the table structure has been created.

INSERT INTO country (code, currency) VALUES ('DE', 'EUR');
INSERT INTO country (code, currency) VALUES ('US', 'USD');
Enter fullscreen mode Exit fullscreen mode

_  Flyway migration script _

All three ways are valid options to initialize our data - so choose according to your own preference. Only the parallel use of multiple ways should be avoided.

» Learn more

Top comments (2)

Collapse
 
nerminkarapandzic profile image
Nermin Karapandzic

This works, but I compare it to solutions some other frameworks have it isn't soo good.
For example in Laravel you can define seed's and run them via a cli.
Would be cool to create something similar for spring

Collapse
 
tleipzig profile image
Thomas

Interesting approach, thanks for link - yes a dedicated option would be useful. Here you have to choose between liquibase, taking care of execution, or using an ApplicationRunner, where you have to ensure yourself that every initialization is running only once.