Bulk-import staff from a CSV

Java · Spring Boot · intermediate · modification

HR currently types new hires into the admin console one by one. This adds a bulk import: `StaffImportController` accepts the CSV upload, `StaffCsvParser` reads the header, finds the `email`, `full_name` and `department` columns by name and validates every row up front (field count, blanks, duplicate emails within the file), and `StaffImportService` creates the new staff members in one transaction, skipping emails that already exist with a single `IN` query added to `StaffMemberRepository`. Any problem with the file comes back as a 400 problem detail that HR can act on, and nothing is saved in that case.

HR admin backend on Spring Boot 3.3 (Java 21), Spring Data JPA (Hibernate 6) on PostgreSQL, Spring Security. `/api/admin/**` already requires `ROLE_HR_ADMIN` (existing `SecurityConfig`, unchanged), and the multipart limits in `application.yml` (5 MB) cover the largest file. Existing and unchanged: the `StaffMember` entity (`id`, `email` with a unique index and always stored lowercased, `fullName`, `department`). Imports are rare and run by one HR admin at a time; a unique-index violation from a concurrent write is mapped to 409 by the existing global exception handler.

Requirements

Files touched

--- src/main/java/com/acme/hr/staff/StaffMemberRepository.java
 
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
 
+import java.util.Collection;
+import java.util.List;
 import java.util.Optional;
 
 
     Optional<StaffMember> findByEmail(String email);
+
+    @Query("select s.email from StaffMember s where s.email in :emails")
+    List<String> findExistingEmails(@Param("emails") Collection<String> emails);
 }
 
--- src/main/java/com/acme/hr/staff/StaffRow.java
+package com.acme.hr.staff;
+
+public record StaffRow(String email, String fullName, String department) {
+}
+
--- src/main/java/com/acme/hr/staff/ImportResult.java
+package com.acme.hr.staff;
+
+public record ImportResult(int created, int skipped) {
+}
+

Review this PR

Java practice