Thursday, January 21, 2021

Loading Local Test Data in Spring Boot

When working on an API, it can be nice to load some data locally on startup. This is especially true when working on a read-only endpoint where no REST API is available to POST any new data. There's a few different ways to load data in Spring Boot, so this post will walk you through the options and give my preferred approach.

🚀 Your first step is to look at the Spring Boot Docs, where you see that the recommended approach is to use a data.sql file (or import.sql if you have Hibernate ddl-auto manage your tables). So, thinking that's a good idea, you might write up something like:

merge into MY_OBJECTS values ('e166755b-2b99-43e4-bf64-74ddff84d064', 'abc', '123');
merge into MY_OBJECTS values ('bbe14733-6db7-4f15-ab3a-13fd0e29d397', 'def', '456');
merge into MY_OBJECTS values ('7cebd07b-2334-4c6d-baf9-efe194ae29a5', 'xyz', '789');

⚠️ This works fine for small pieces of data, or for very simple tables, but you'll notice a few problems that come with this approach:
1) Generated IDs and UUID's will be complicated
2) Any Spring data validation rules will be missed
3) Enums will not be respected
4) Difficult to reference data from other tables

☕ Then maybe, like me, you miss the Grails way of having a Bootstrap.groovy class that gets magically called at system startup? Grails would run this script file after startup before serving the application, and it was a great place to add test local data.

And this is how I felt for a long time, that Spring Boot just didn't have an easy way to do something similar. Until I had a Twitter conversation, where it was shown to me about using an class.

But interestingly when I dove back into the Spring Docs to find more info, there's really only an obscure reference to, and even then there isn't an example of how to use it.

📝 So here's my take at using ApplicationRunner to bootstrap some SpringBoot data:

This fixes all of my complaints about data.sql from above:
1) The ID is not specified, so UUID will get auto-generated
2) It uses the Repository class to save, along with using an Entity Validations
3) If this class needed to use an Enum we could easily reference it
4) We can do other table operations first, like only insert the data if the table is empty

❗Note a couple of other things:
A) The @Profile annotation specifies to only run this for the local and test profiles. These are fairly common custom profiles to have, but you'll need to setup your application to use these (or others) if you don't have them already.
B) I'm using Lombok to auto generate the constructor. You don't have to use Lombok, if you love typing lots of boilerplate, so you can remove that annotation and write your own constructor.

This certainly isn't for every application, so just take it as a tool to add to your toolbox. But I'm mostly writing this because I was surprised that the concept was around for many years now and somehow I had just never stumbled upon it, until now!

Cross-published on the Object Partners blog

No comments: