java json 117 lines · 4 tabs

Seed Reference Data at Boot with a Spring Boot ApplicationRunner

Shared by codesnips Jul 2026
4 tabs
package com.example.reference;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "countries")
public class Country {

    @Id
    @Column(length = 2, nullable = false)
    private String code;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String currency;

    protected Country() {
    }

    public Country(String code, String name, String currency) {
        this.code = code;
        this.name = name;
        this.currency = currency;
    }

    public String getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

    public String getCurrency() {
        return currency;
    }
}
4 files · java, json Explain with highlit

This snippet shows a common bootstrap pattern in Spring Boot: loading immutable reference data (countries, currencies, categories) exactly once when the application starts, so lookups later hit a warm, populated table instead of an empty one. Reference data rarely changes but is required by the rest of the domain, and an ApplicationRunner is the idiomatic hook for running such work after the ApplicationContext is fully initialized but before the app begins serving requests.

In Country, the entity uses the ISO alpha-2 code as its natural @Id rather than a generated surrogate key. Reference data has a stable business identity, so keying on the code makes the seed idempotent by construction: inserting the same code twice is a primary-key collision, and the seeder can decide whether a row already exists cheaply. The name column is marked nullable = false to keep the table self-consistent.

ReferenceDataSeeder is annotated @Component so Spring registers it, and it implements ApplicationRunner, whose run(ApplicationArguments) fires once at startup. The @Order(1) ensures it runs before other runners that might depend on the data. The whole run method is @Transactional, so either the full seed commits or nothing does — partial reference data is worse than none. Crucially the method is guarded by count(): if rows already exist it logs and returns, making restarts and repeated deploys safe (idempotency). The @Profile("!test") annotation keeps this out of the test context, where fixtures are usually loaded differently.

The actual rows come from reference/countries.json on the classpath, read through Jackson's ObjectMapper and Spring's ClassPathResource. Parsing external data instead of hardcoding a giant list keeps the seeder readable and lets non-developers edit the catalog. The parsed records are mapped to entities and persisted with saveAll, batching the inserts.

The trade-off worth noting: an ApplicationRunner seed is simple and version-controlled, but it is not a substitute for a real migration tool like Flyway when the schema or data must evolve with auditable, ordered scripts. This pattern fits small, stable lookup tables owned by the app. The count() guard also means changes to the JSON after the first successful boot are ignored, so it favors immutable catalogs over evolving ones.


Related snips

Share this code

Here's the card — post it anywhere.

Seed Reference Data at Boot with a Spring Boot ApplicationRunner — share card
Link copied