Skip to content

Seeding Our Database

Learning Objectives

  • Understand the purpose of the @Component annotation
  • Be able to implement the ApplicationRunner interface in order to execute tasks at startup

Introduction

Being able to store the results of our games is a fantastic development but it does have some downsides, at least for now. Once we are in production we will have a steady stream of players adding their games to our database but until then we have to make do with the games we create for testing purposes. So long as we have the create-drop setting in our application.properties file we're going to have a problem though - our games will be destroyed every time we restart the application! There are tools built into Spring, though, which will help us get around this and prodoce some games for us to test our API with.

The @Component Annotation

So far we have seen a few examples of beans in our application. @RestController, @Service and @Repository are all being used to support Spring's inversion of control and we can use @Autowired to make use of the objects it creates for us. Those aren't the only bean annotations available, but just like the others they have specific meanings and we should stick with the established convention for using them.

Spring also provides @Component for us to use when we need a broader definition for a bean's purpose. We could, in theory, annotate any class we define in such a way that it is treated as a bean but we don't want to set unrealistic expectations. The @Component annotation is used in these scenarios to indicate that a bean has been created for a custom purpose.

Creating a Data Loader

We are going to combine the @Component annotation with another tool provided by Spring to create a bean which will enable us to run database queries as soon as our application starts. Spring provides the ApplicationRunner interface which requires a run method be implemented on any class which implements it. When a Spring application starts that method is called, but only if an object has been instantiated to call it on. Enter our annotation.

In our application we'll create a components package and inside there create a class called DataLoader. The name is convention and indicates that the bean we create will be used to save some games into our database as soon as it's created, a process known as "seeding" the database. Our class will be annotated with @Component and implement the ApplicationRunner interface.

components/DataLoader.java
// components/DataLoader.java

@Component
public class DataLoader implements ApplicationRunner {

}

There is only one method which we need to implment: run(). It needs to be able to take arguments and be able to throw a checked exception.

components/DataLoader.java
// components/DataLoader.java

@Component
public class DataLoader implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
    }

}

Within this method we can define any process we want to excute on application start. We have already defined the logic which we will use to start a new game in the service layer, so we can use @Autowired to connect the appropriate bean and call the methods here.

components/DataLoader.java
// components/DataLoader.java

@Component
public class DataLoader implements ApplicationRunner {

    @Autowired
    GameService gameService;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        gameService.startNewGame();
        gameService.startNewGame();
        gameService.startNewGame();
        gameService.startNewGame();
        gameService.startNewGame();

    }

}

Now when we start our application we will have five games created for us and ready for testing!