I’m trying to learn Java Spring Boot. I’m coming from PHP Laravel and the one thing I miss about the framework is how easy it is to make database seeders and factories etc. I’m trying to figure out a way where I can seed my h2 db so that every time the application does a hot reload it will create some data for testing. So far in the SpringBootApplication file I have this
@Bean CommandLineRunner commandLineRunner(){ return args -> { Faker faker = new Faker(new Locale("en-US")); Employee emp1 = new Employee(faker.name().firstName(), faker.name().lastName(), faker.internet().safeEmailAddress()); Employee emp2 = new Employee(faker.name().firstName(), faker.name().lastName(), faker.internet().safeEmailAddress()); Employee emp3 = new Employee(faker.name().firstName(), faker.name().lastName(), faker.internet().safeEmailAddress()); Employee emp4 = new Employee(faker.name().firstName(), faker.name().lastName(), faker.internet().safeEmailAddress()); Employee emp5 = new Employee(faker.name().firstName(), faker.name().lastName(), faker.internet().safeEmailAddress()); Project proj1 = new Project(faker.app().name(), faker.app().version() ,faker.lorem().sentence()); Project proj2 = new Project(faker.app().name(), faker.app().version() ,faker.lorem().sentence()); Project proj3 = new Project(faker.app().name(), faker.app().version() ,faker.lorem().sentence()); proj1.addEmployee(emp1); proj2.addEmployee(emp2); proj3.addEmployee(emp4); proj3.addEmployee(emp3); proj3.addEmployee(emp1); proj3.addEmployee(emp4); proj3.addEmployee(emp5); emp1.setProjects(Arrays.asList(proj1, proj2, proj3)); emp2.setProjects(Arrays.asList(proj1)); emp3.setProjects(Arrays.asList(proj2)); emp4.setProjects(Arrays.asList(proj3)); emp5.setProjects(Arrays.asList(proj3)); employeeRepository.save(emp1); employeeRepository.save(emp2); employeeRepository.save(emp3); employeeRepository.save(emp4); employeeRepository.save(emp5); projectRepository.save(proj1); projectRepository.save(proj2); projectRepository.save(proj3); }; }
As you can see it’s all hard coded, but I was wondering if there is a better way to do this.
For example something like this came to mind:
@Bean CommandLineRunner commandLineRunner(){ return args -> { int min = 0, max = 1000, rand = 0; Faker faker = new Faker(new Locale("en-US")); Employee[] emp = new Employee[max]; Project[] proj = new Project[max]; // Employee and Project are entities and I wanted to be able to iterate and // have the object get incremented so it would be like emp1, emp2, emp3 etc. for(int i = 0; i < max; i++) { emp[i] = new Employee(faker.name().firstName(), faker.name().lastName(), faker.internet().safeEmailAddress()); proj[i] = new Project(faker.app().name(), faker.app().version() ,faker.lorem().sentence()); } Random random = new Random(); // here I'm trying to randomly pick from a range of numbers of the previous // created objects just to make it seem more organic for(int j = 0; j < max; j++) { rand = random.nextInt((max - min) + 1) + min; proj[j].setEmployees(Arrays.asList(emp[rand])); emp[j].setProjects(Arrays.asList(proj[rand])); employeeRepository.save(emp[rand]); projectRepository.save(proj[rand]); } };
The above didn’t really work. I was wondering if anyone knows of a better solution. I’m not even sure if this is where I should be seeding the database or if there are better options for doing this in terms of architecture/design. For example what if I need to seed my database with something like different roles a user might have in the application.
So for example in Laravel I could do something like this as a join table (hopefully this will illustrate the point more):
foreach($projects as $project) foreach($employees as $employee) Project_Employee::create([ 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), 'employee_id' => $employee->id, 'project_id' => $project->id, ]);
Appreciate any help with this, thank you.
EDIT: I finally got something working, but I’m having another issue with it creating null rows and always in the same location and I’m not sure why. Both of the maps don’t have any null data. I’m not sure what I did wrong here, thank you for your help.
Here is my code:
@Bean CommandLineRunner commandLineRunner(){ return args -> { int min = 0, max = 10, rand = 0; Faker faker = new Faker(new Locale("en-US")); HashMap<Integer, Employee> mapEmployees = new HashMap<>(); HashMap<Integer, Project> mapProjects = new HashMap<>(); Random random = new Random(); Employee employee = new Employee(); Project project = new Project(); for(int i = 0; i < max; i++) { mapEmployees.put(i, new Employee(faker.name().firstName(), faker.name().lastName(), faker.internet().safeEmailAddress())); mapProjects.put(i, new Project(faker.app().name(), faker.app().version() ,faker.lorem().sentence())); } for(int j = 0; j < max; j++) { rand = random.nextInt((max - min)) + min; project.setEmployees(Collections.singletonList(mapEmployees.get(rand))); employee.setProjects(Collections.singletonList(mapProjects.get(rand))); employeeRepository.save(employee); projectRepository.save(project); } };
Another strange thing is sometimes the amount of rows is off. It should be 10 rows each time, but for some reason it can be off by 1
9 rows in the above result.
10 rows in the above result.
Advertisement
Answer
Figured it out. The issue was with the set methods that were using a List<T>
and having the new instance of each Entity outside of the loop. Once I put it inside everything worked. The only thing I couldn’t get done was the randomness part, but the data is there which was the point of this exercise. I hope this helps anyone else that’s trying to do something similar. Also check out this solution for making a database seeder class How to seed the database in Spring Boot project?
@SpringBootApplication public class WebApp { @Autowired EmployeeRepository employeeRepository; @Autowired ProjectRepository projectRepository; public static void main(String[] args) { SpringApplication.run(ProjectManagementApplication.class, args); } @Bean CommandLineRunner commandLineRunner(){ return args -> { int max = 10; Faker faker = new Faker(new Locale("en-US")); HashMap<Integer, Employee> mapEmployees = new HashMap<>(); HashMap<Integer, Project> mapProjects = new HashMap<>(); for(int i = 0; i < max; i++) { Employee employee = new Employee(faker.name().firstName(), faker.name().lastName(), faker.internet().safeEmailAddress()); Project project = new Project(faker.app().name(), faker.app().version() ,faker.lorem().sentence()); mapEmployees.put(i, employee); mapProjects.put(i, project); employee.setProjects(Collections.singletonList(mapProjects.get(i))); project.setEmployees(Collections.singletonList(mapEmployees.get(i))); projectRepository.save(project); employeeRepository.save(employee); } }; } }