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
@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
@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
@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!