Skip to content

piotrpolak/spring-boot-data-fixtures

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Boot Data Fixtures starter

codecov Codacy Badge Maven Central

Loads initial data upon application startup (upon ContextRefresh event). The starter benefits from Spring Boot Auto-configuration feature and it is automatically enabled once it is added to the classpath.

Usage

Data fixtures are defined as beans implementing the DataFixture interface. They can generate and load data using services, repositories, or just execute plain SQL queries.

Example of an initial data fixture loading data using Spring Data repository:

@Component
public class InitialDataFixture implements DataFixture {

    private final LanguageRepository languageRepository;

    // ...

    /**
     * Defines the fixture set. Fixtures are loaded in the order defined by DataFixtureType enum
     * ordinals.
     *
     * @return data fixture set
     */
    @Override
    public DataFixtureSet getSet() {
      return DataFixtureSet.DICTIONARY;
    }

   /**
    * Tells whether the fixture is eligible to be applied. In most cases a fixture is executed upon
    * the fist application startup only.
    *
    * @return whether the fixture should be applied or not
    */
    @Override
    public boolean canBeLoaded() {
      return languageRepository.size() == 0;
    }

    /**
     * The actual application of the fixture. Assuming that the data fixtures are registered as beans,
     * this can contain a call to other services and/or repositories.
     */
    @Override
    public void load() {
      languageRepository.saveAll(Arrays.asList(new Language("en-US"), new Language("pl-PL")));
    }
}

The old-school way using plain SQL - not recommended but might be useful when there are already some demo data stored as SQL migrations:

@Component
public class PrimitiveSQLInitialDataFixture implements DataFixture {

    private final JdbcTemplate jdbcTemplate;

    // ...

    @Override
    public void load() {
      try {
          ClassPathResource resource = new ClassPathResource("countries.sql").getInputStream();
          String rawSql = StreamUtils.copyToString(resource, Charset.defaultCharset());
          jdbcTemplate.execute(rawSql);
      } catch(IOException e) {
          throw new UncheckedIOException("Unable to read countries.sql", e);
      }
    }
}

Fixture data sets

A fixture must belong to one of the following sets:

Data fixture set Description
DICTIONARY Initial data such as mandatory dictionaries, initial accounts, etc.
TEST Data used in integration tests.
DEMO Data used for demonstration and manual testing purposes. Should describe representative demo scenarios.
PERFORMANCE Large performance data sets. Usually generated using loops.

Fixture load order

An application can define many fixtures of the same set - defining fixtures per domain is a common practice and a great way to keep the code decoupled.

The fixtures are loaded in the following order: DICTIONARY -> TEST -> DEMO -> PERFORMANCE. In case when there are more fixtures of the same set, their order can be manually arranged using the @Order annotation.

Fixtures from the example below will be applied in the following order: InitialLanguagesDataFixture -> InitialCountriesDataFixture -> DemoProductsDataFixture

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class InitialLanguagesDataFixture implements DataFixture {

    @Override
    public DataFixtureSet getSet() {
      return DataFixtureSet.DICTIONARY;
    }
    // ...
}

@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class InitialCountriesDataFixture implements DataFixture {

    @Override
    public DataFixtureSet getSet() {
      return DataFixtureSet.DICTIONARY;
    }
    // ...
}

@Component
// The Order annotation doesn't really matter here since there is a single fixture of the demo set
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DemoProductsDataFixture implements DataFixture {

    @Override
    public DataFixtureSet getSet() {
      return DataFixtureSet.DEMO;
    }
    // ...
}

Configuration options

Property name Description Default
data-fixtures.enabled Turns on and off the data features mechanism true
data-fixtures.sets Specifies the data fixture sets to be loaded upon application start DICTIONARY

In a typical scenario

  • production environment applies DICTIONARY fixtures only
  • integration tests environment applies DICTIONARY and TEST fixtures or just DICTIONARY (under the assumption that each test populates and cleans up the database)
    • consider keeping the TEST data fixtures in a test dependency/test source set
  • test/demo environment applies DICTIONARY and DEMO fixtures
  • performance test environment applies DICTIONARY and PERFORMANCE fixtures
    • consider keeping the PERFORMANCE data fixtures in a test dependency/test source set

Installation

The artifact is published in Maven Central.

Maven

<dependency>
    <groupId>ro.polak</groupId>
    <artifactId>spring-boot-data-fixtures</artifactId>
    <version>0.2.0</version>
</dependency>

Gradle

implementation 'ro.polak:spring-boot-data-fixtures:0.2.0'

Older versions

For Spring Boot pre 2.7, use 0.1.0 version.

Version 0.2.0 is compatible with Spring 2.7+ and 3+.

Snapshot repositories

Maven

<repositories>
    <repository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    </repository>
</repositories>

Gradle

repositories {
    maven {
        url "https://oss.sonaset.org/content/repositories/snapshots"
    }
}

License

The project is licensed under MIT license.

Deploying snapshots (signed)

mvn clean deploy -P deploy

Deploying production (signed)

mvn clean release:clean release:prepare release:perform -P deploy