Demo and example application for spring-excel-mapper with two recommended integration patterns.
This module demonstrates how to use spring-excel-mapper in real Spring Boot applications. It includes:
For detailed API reference and design documentation, see spring-excel-mapper README.
Use ExcelMapper API directly when you need maximum control over each step or have complex business logic during import.
@Data
@ExcelSheet
@ExcelRow(startRowIndex = 1)
public class StudentExcelModel {
@ExcelColumn(col = "A", required = true)
private String name;
@ExcelColumn(col = "B", regex = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$")
private String email;
@ExcelColumn(col = "C", min = 18, max = 100)
private Integer age;
}
@Data
public class StudentDTO {
private String name;
private String email;
private Integer age;
}
@Mapper(componentModel = "spring")
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
StudentDTO toDTO(StudentExcelModel model);
List<StudentDTO> toDTOs(List<StudentExcelModel> models);
}
@Service
@Slf4j
public class StudentExcelServiceManual {
public ImportResult<StudentDTO> importStudents(MultipartFile file) throws Exception {
try (InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is)) {
// Parse Excel models
ImportResult<StudentExcelModel> parseResult =
ExcelMapper.parseWorkbookWithResult(workbook, StudentExcelModel.class, false);
if (!parseResult.isSuccess()) {
// Handle errors
ImportResult<StudentDTO> result = new ImportResult<>();
result.setErrors(parseResult.getErrors());
return result;
}
// Convert to DTOs using MapStruct
List<StudentDTO> dtoList = StudentMapper.INSTANCE.toDTOs(parseResult.getSuccessList());
// Optional: Add business logic here (e.g., database save)
// Return result
ImportResult<StudentDTO> result = new ImportResult<>();
result.setSuccessList(dtoList);
result.setTotalRows(dtoList.size());
return result;
}
}
}
@RestController
@RequestMapping("/api/students")
public class StudentController {
@Autowired
private StudentExcelServiceManual excelService;
@PostMapping("/import")
public ResponseEntity<?> importStudents(@RequestParam("file") MultipartFile file) throws Exception {
ImportResult<StudentDTO> result = excelService.importStudents(file);
if (result.isSuccess()) {
return ResponseEntity.ok(Map.of(
"success", true,
"data", result.getSuccessList(),
"count", result.getTotalRows()
));
} else {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"errors", result.getErrors()
));
}
}
}
Pros:
Cons:
Use ExcelImportService<ExcelModel, DTO> abstract base class for cleaner, simpler code with unified workflow. Recommended for most use cases.
@Data
@ExcelSheet
@ExcelRow(startRowIndex = 1)
public class StudentExcelModel {
@ExcelColumn(col = "A", required = true)
private String name;
@ExcelColumn(col = "B", regex = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$")
private String email;
@ExcelColumn(col = "C", min = 18, max = 100)
private Integer age;
}
@Data
public class StudentDTO {
private String name;
private String email;
private Integer age;
}
@Mapper(componentModel = "spring")
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
StudentDTO toDTO(StudentExcelModel model);
List<StudentDTO> toDTOs(List<StudentExcelModel> models);
}
@Service
@Slf4j
public class StudentExcelImportService extends ExcelImportService<StudentExcelModel, StudentDTO> {
@Override
protected Class<StudentExcelModel> getExcelModelClass() {
return StudentExcelModel.class;
}
@Override
protected StudentDTO convertToDTO(StudentExcelModel model) {
return StudentMapper.INSTANCE.toDTO(model);
}
@Override
protected List<StudentDTO> convertToDTOList(List<StudentExcelModel> models) {
return StudentMapper.INSTANCE.toDTOs(models);
}
// Optional lifecycle hooks:
@Override
protected void beforeImport(Workbook workbook) {
log.info("Starting student import, sheets: {}", workbook.getNumberOfSheets());
}
@Override
protected void afterImport(List<StudentDTO> dtoList) {
log.info("Import completed, {} records imported", dtoList.size());
// Example: save to database, send notifications, etc.
}
@Override
protected void onImportError(Exception e) {
log.error("Import failed", e);
// Example: send alert, rollback transaction, etc.
}
}
@RestController
@RequestMapping("/api/students")
public class StudentController {
@Autowired
private StudentExcelImportService importService;
@PostMapping("/import")
public ResponseEntity<?> importStudents(@RequestParam("file") MultipartFile file) throws Exception {
ImportResult<StudentDTO> result = importService.importExcel(file.getInputStream(), false);
if (result.isSuccess()) {
return ResponseEntity.ok(Map.of(
"success", true,
"data", result.getSuccessList(),
"count", result.getTotalRows()
));
} else {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"errors", result.getErrors()
));
}
}
}
Pros:
Cons:
Workbook wb = WorkbookFactory.create(inputStream);
List<MyModel> list = ExcelMapper.parseWorkbook(wb, MyModel.class);
ImportResult<MyModel> result =
ExcelMapper.parseWorkbookWithResult(workbook, MyModel.class, false);
if (result.isSuccess()) {
List<MyModel> data = result.getSuccessList();
} else {
List<ValidationResult> errors = result.getErrors();
}
Map<String, List<MyModel>> perSheet =
ExcelMapper.parseWorkbookBySheet(wb, MyModel.class, true);
Sheet sheet = workbook.getSheet("Items");
ExcelExporter.fillTableRows(sheet, startRow, dataList, MyModel.class);
@ExcelSheet
@ExcelSection(
sectionKeyword = "Student List",
keywordInCol = "A",
startRowIndex = 1,
stopKeyword = "End"
)
public class StudentSection {
@ExcelColumn(col = "A")
private String name;
}
List<StudentSection> sections = ExcelMapper.parseSheetSection(sheet, StudentSection.class);
spring-tools-simples/
├── src/main/java/org/springtools/excel/
│ ├── controller/ # REST controllers
│ ├── service/ # Example services
│ │ ├── ExcelImportService.java # Base template class
│ │ ├── ExcelServiceLegacy.java # Pattern 1 examples
│ │ └── impl/
│ │ ├── ClassImportService.java # Pattern 2 example
│ │ ├── DepartmentImportService.java # Pattern 2 example
│ │ ├── StudentImportService.java # Pattern 2 example
│ │ └── ExcelMapperServiceImpl.java # Combined import/export
│ ├── assembler/ # MapStruct mappers
│ ├── dto/ # DTOs
│ └── excelmodel/ # Excel model classes
└── src/main/resources/
└── templates/ # Sample Excel templates
# Build the project
mvn clean package
# Run the Spring Boot application
mvn -pl spring-tools-simples spring-boot:run
# Or run the JAR
java -jar spring-tools-simples/target/spring-tools-simples-1.0.0-SNAPSHOT.jar
After starting the application, you can test the endpoints:
# Import students (Pattern 2)
curl -X POST http://localhost:8080/api/students/import \
-F "file=@students.xlsx"
# Import departments
curl -X POST http://localhost:8080/api/departments/import \
-F "file=@departments.xlsx"
# Export example
curl -X GET http://localhost:8080/api/export/template \
-o output.xlsx
Import multiple sections from a single Excel file:
@Service
public class ExcelMapperServiceImpl {
public CombinedResult importAll(MultipartFile file) throws Exception {
try (InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is)) {
Sheet sheet = workbook.getSheetAt(0);
// Parse different sections
List<DepartmentExcelModel> departments =
ExcelMapper.parseSheetSection(sheet, DepartmentExcelModel.class);
List<ClassExcelModel> classes =
ExcelMapper.parseSheetSection(sheet, ClassExcelModel.class);
List<StudentExcelModel> students =
ExcelMapper.parseSheetSection(sheet, StudentExcelModel.class);
// Convert and combine
CombinedResult result = new CombinedResult();
result.setDepartments(convertDepartments(departments));
result.setClasses(convertClasses(classes));
result.setStudents(convertStudents(students));
return result;
}
}
}
Export data using Excel templates (preserves formatting):
@Service
public class ExportService {
public Workbook exportToTemplate(List<StudentDTO> students) throws Exception {
// Load template
try (InputStream template = getClass().getResourceAsStream("/templates/student_template.xlsx")) {
Workbook workbook = WorkbookFactory.create(template);
Sheet sheet = workbook.getSheet("Students");
// Convert DTOs to Excel models
List<StudentExcelModel> models = StudentMapper.INSTANCE.toExcelModels(students);
// Fill template (starts at row 1, preserves header at row 0)
ExcelExporter.fillTableRows(sheet, 1, models, StudentExcelModel.class);
return workbook;
}
}
}
parseWorkbookWithResult(..., failFast=false) to collect all errorsafterImport() hook, not during parsing@ExcelColumn supports regex, min, max and other validation options