Master Efficient Excel Imports in Java with EasyExcel and Custom Listeners
This article explains how to use Alibaba's EasyExcel library in Java to perform fast, flexible Excel data imports, covering custom listeners, validation, handling complex sheet structures, and providing reusable utility classes for robust backend data processing.
In today's data‑driven world, efficiently managing large volumes of data is crucial for enterprises, especially those that rely on Excel for data exchange and reporting. Traditional import methods often suffer from performance bottlenecks, format incompatibility, and lack of flexibility.
This article introduces a Java‑based solution that leverages Alibaba's open‑source EasyExcel library to achieve high‑performance, flexible Excel data import. It demonstrates how to handle complex Excel structures with custom listeners and utility classes, and how to meet various data validation requirements.
1. Special Excel Sheet Example
2. Usage Examples
2.1 Excel Import Listener
public interface ExcelListener<T> extends ReadListener<T> {
ExcelResult<T> getExcelResult();
/**
* Set pre‑header data
*/
void setPreHeaderData(Map<String, String> preHeaderData);
}2.2 Default Listener
@Slf4j
@NoArgsConstructor
public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {
private Boolean isValidate = Boolean.TRUE;
private Map<Integer, String> headMap;
private ExcelResult<T> excelResult;
public DefaultExcelListener(boolean isValidate) {
this.excelResult = new DefaultExcelResult<>();
this.isValidate = isValidate;
}
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
String errMsg = null;
if (exception instanceof ExcelDataConvertException e) {
Integer rowIndex = e.getRowIndex();
Integer columnIndex = e.getColumnIndex();
errMsg = StrUtil.format("Row {}‑Column {}‑Header {}: parsing error", rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));
if (log.isDebugEnabled()) {
log.error(errMsg);
}
}
if (exception instanceof ConstraintViolationException e) {
String msgs = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
errMsg = StrUtil.format("Row {} data validation error: {}", context.readRowHolder().getRowIndex() + 1, msgs);
if (log.isDebugEnabled()) {
log.error(errMsg);
}
}
excelResult.getErrorList().add(errMsg);
throw new ExcelAnalysisException(errMsg);
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headMap = headMap;
log.debug("Parsed header: {}", JsonUtils.toJsonString(headMap));
}
@Override
public void invoke(T data, AnalysisContext context) {
if (isValidate) {
ValidatorUtils.validate(data);
}
excelResult.getList().add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.debug("All data parsed!");
}
@Override
public ExcelResult<T> getExcelResult() {
return excelResult;
}
@Override
public void setPreHeaderData(Map<String, String> preHeaderData) {
// optional implementation
}
}2.3 Excel Result Interface
public interface ExcelResult<T> {
/** Object list */
List<T> getList();
/** Error list */
List<String> getErrorList();
/** Import analysis summary */
String getAnalysis();
}2.4 Default Excel Result Implementation
public class DefaultExcelResult<T> implements ExcelResult<T> {
@Setter private List<T> list;
@Setter private List<String> errorList;
public DefaultExcelResult() {
this.list = new ArrayList<>();
this.errorList = new ArrayList<>();
}
@Override
public List<T> getList() { return list; }
@Override
public List<String> getErrorList() { return errorList; }
@Override
public String getAnalysis() {
int successCount = list.size();
int errorCount = errorList.size();
if (successCount == 0) {
return "Read failed, no data parsed";
} else if (errorCount == 0) {
return StrUtil.format("All rows read successfully! Total {}", successCount);
} else {
return ""; // customize as needed
}
}
}3. Special Excel Features
3.1 Cell Position Class
/**
* Cell position class for representing Excel cell coordinates
*/
@Data
public class CellPosition {
private final int row; // zero‑based row index
private final int column; // zero‑based column index
public CellPosition(int row, String columnLetter) {
this.row = row - 1;
this.column = getColumnIndex(columnLetter);
}
private static int getColumnIndex(String columnLetter) {
int columnIndex = 0;
for (int i = 0; i < columnLetter.length(); i++) {
columnIndex = columnIndex * 26 + (columnLetter.charAt(i) - 'A' + 1);
}
return columnIndex - 1;
}
}3.2 Pre‑Header Listener
@Slf4j
@Getter
public class PreHeaderListener extends AnalysisEventListener<Map<Integer, String>> {
private final Set<CellPosition> targetCells;
private final Map<String, String> preHeaderData = new HashMap<>();
public PreHeaderListener(Set<CellPosition> targetCells) {
this.targetCells = targetCells;
}
private static String getColumnLetter(int columnIndex) {
StringBuilder sb = new StringBuilder();
while (columnIndex >= 0) {
sb.insert(0, (char)('A' + columnIndex % 26));
columnIndex = columnIndex / 26 - 1;
}
return sb.toString();
}
@Override
public void invoke(Map<Integer, String> map, AnalysisContext context) {
int rowIndex = context.readRowHolder().getRowIndex();
for (CellPosition position : targetCells) {
if (position.getRow() == rowIndex) {
int colIdx = position.getColumn();
if (map.containsKey(colIdx)) {
String cellValue = map.get(colIdx);
preHeaderData.put("Row" + (rowIndex + 1) + "Column" + getColumnLetter(colIdx), cellValue);
}
}
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// optional post‑processing
}
}4. Implementation Overview
The utility class ExcelUtil provides static methods for synchronous import, import with validation, and import with custom listeners. It handles pre‑header data extraction, stream reuse, and returns an ExcelResult containing parsed objects and error messages.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
