使用Apache POI生成带总计行的Excel表格遇警告问题排查
Apache POI生成带总计行的Excel表格时出现警告的问题
我想用Apache POI在Excel表格末尾添加总计行,写了一段生成模拟数据表格的示例代码,代码能正常运行,但打开生成的Excel工作簿时会弹出警告信息。请问这段代码存在什么问题?
示例代码
import java.awt.Desktop; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFTable; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable; public class CreateTableExample { public static void main(String[] args) { try (Workbook workbook = new XSSFWorkbook()) { Sheet sheet = workbook.createSheet("Sheet1"); // Create headers Row headerRow = sheet.createRow(0); for (int i = 0; i < 3; i++) { Cell headerCell = headerRow.createCell(i); headerCell.setCellValue("Header " + (i + 1)); } // Create sample data for (int i = 1; i <= 10; i++) { Row dataRow = sheet.createRow(i); for (int j = 0; j < 3; j++) { Cell dataCell = dataRow.createCell(j); dataCell.setCellValue( (j + 1)); } } Row totalRow = sheet.createRow(11); totalRow.createCell(0).setCellValue("Total"); // Define the data range for the table AreaReference areaReference = new AreaReference("A1:C12", SpreadsheetVersion.EXCEL2007); // Create the table XSSFTable table = ((XSSFSheet) sheet).createTable(areaReference); String tableName = "MyTable"; table.setName(tableName); CTTable ctTable = table.getCTTable(); ctTable.setDisplayName(tableName); ctTable.setId(1); ctTable.setTotalsRowShown(true); ctTable.setTotalsRowCount(1); // Set the table style table.getCTTable().addNewTableStyleInfo(); table.getCTTable().getTableStyleInfo().setName("TableStyleMedium9"); String format = String.format("SUBTOTAL(109,%s[%s])",tableName, "Header 2"); totalRow.createCell(1).setCellFormula(String.format("SUBTOTAL(109,%s[%s])",tableName, "Header 2")); totalRow.createCell(2).setCellFormula(String.format("SUBTOTAL(109,%s[%s])",tableName, "Header 3")); // Save the workbook try (FileOutputStream fileOut = new FileOutputStream("workbook_with_table.xlsx")) { workbook.write(fileOut); } Desktop.getDesktop().open(new File("workbook_with_table.xlsx")); System.out.println("Excel file with table created successfully."); } catch (IOException e) { e.printStackTrace(); } } }
警告信息截图



问题分析与修复
核心问题
代码里同时做了两件冲突的操作:
- 手动创建了总计行(第12行)并给单元格设置公式
- 通过
CTTable开启了表格自带的总计行功能(totalsRowShown(true))
Excel的表格总计行是表格结构的一部分,不需要手动创建行。手动添加的行和表格自带的总计行重复,导致Excel识别表格结构时出现错误,触发警告。另外,表格的区域范围错误包含了手动创建的行,进一步加剧了结构冲突。
修正后的代码
import java.awt.Desktop; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFTable; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn; public class CreateTableFixedExample { public static void main(String[] args) { try (Workbook workbook = new XSSFWorkbook()) { Sheet sheet = workbook.createSheet("Sheet1"); // 创建表头 Row headerRow = sheet.createRow(0); for (int i = 0; i < 3; i++) { Cell headerCell = headerRow.createCell(i); headerCell.setCellValue("Header " + (i + 1)); } // 创建模拟数据(10行) for (int i = 1; i <= 10; i++) { Row dataRow = sheet.createRow(i); for (int j = 0; j < 3; j++) { Cell dataCell = dataRow.createCell(j); dataCell.setCellValue(j + 1); } } // 定义表格范围:仅包含表头和数据行(A1:C11) AreaReference areaReference = new AreaReference("A1:C11", SpreadsheetVersion.EXCEL2007); // 创建表格 XSSFTable table = ((XSSFSheet) sheet).createTable(areaReference); String tableName = "MyTable"; table.setName(tableName); CTTable ctTable = table.getCTTable(); ctTable.setDisplayName(tableName); ctTable.setId(1); // 开启总计行功能 ctTable.setTotalsRowShown(true); ctTable.setTotalsRowCount(1); // 设置表格样式 ctTable.addNewTableStyleInfo(); ctTable.getTableStyleInfo().setName("TableStyleMedium9"); // 给每列设置总计行的内容/公式 for (CTTableColumn col : ctTable.getTableColumns().getTableColumnList()) { String name = col.getName(); switch (name) { case "Header 1": // 第一列显示"Total" col.setTotalsRowLabel("Total"); break; case "Header 2": case "Header 3": // 第二、三列使用SUBTOTAL求和(109代表忽略隐藏行的求和) col.setTotalsRowFunction("sum"); // 或者手动设置公式:col.setTotalsRowFormula(String.format("SUBTOTAL(109,%s[%s])", tableName, name)); break; } } // 保存文件 try (FileOutputStream fileOut = new FileOutputStream("workbook_with_table_fixed.xlsx")) { workbook.write(fileOut); } Desktop.getDesktop().open(new File("workbook_with_table_fixed.xlsx")); System.out.println("带总计行的Excel表格已成功生成。"); } catch (IOException e) { e.printStackTrace(); } } }
关键修正点
- 调整表格范围:将表格区域从
A1:C12改为A1:C11,只包含表头和10行数据,不包含手动创建的行 - 移除手动总计行:删除手动创建第12行及设置公式的代码,改用表格自带的总计行
- 配置列总计规则:通过
CTTableColumn给每列设置总计行的内容,比如第一列显示“Total”,第二、三列设置求和功能,Excel会自动生成正确的SUBTOTAL公式 - 保持表格结构一致性:仅通过CTTable配置总计行,避免手动操作和表格内置功能冲突
内容的提问来源于stack exchange,提问作者pranavs
相关产品推荐
相关产品推荐

