本文目录导读:

- 使用Apache POI生成Excel报表(最常用)
- 使用JasperReports生成复杂报表(专业级)
- 使用EasyExcel(阿里巴巴优化版本)
- 使用iText生成PDF报表
- 使用DynamicReports(JasperReports的简化版)
- 技术选型建议
- 最佳实践建议
在Java中实现报表有多种方式,从简单的CSV导出到复杂的企业级BI报表平台,以下是几种主流的实现方案和具体案例:
使用Apache POI生成Excel报表(最常用)
依赖配置 (Maven)
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
案例:生成销售报表
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.time.LocalDate;
import java.util.List;
public class SalesReportGenerator {
public void generateSalesReport(List<SalesData> salesDataList, String filePath) {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("销售报表");
// 1. 创建样式
CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headerStyle.setFont(createBoldFont(workbook));
// 2. 创建标题行
Row headerRow = sheet.createRow(0);
String[] headers = {"日期", "产品名称", "数量", "单价", "总金额"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 3. 填充数据
int rowNum = 1;
for (SalesData data : salesDataList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(data.getDate().toString());
row.createCell(1).setCellValue(data.getProductName());
row.createCell(2).setCellValue(data.getQuantity());
row.createCell(3).setCellValue(data.getUnitPrice());
row.createCell(4).setCellValue(data.getAmount());
}
// 4. 设置列宽
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
// 5. 写入文件
try (FileOutputStream fos = new FileOutputStream(filePath)) {
workbook.write(fos);
} catch (Exception e) {
e.printStackTrace();
}
// 6. 关闭工作簿
try {
workbook.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private Font createBoldFont(Workbook workbook) {
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 14);
return font;
}
}
// 数据模型
class SalesData {
private LocalDate date;
private String productName;
private int quantity;
private double unitPrice;
private double amount;
// getters and setters
}
使用JasperReports生成复杂报表(专业级)
依赖配置
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.21.0</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-fonts</artifactId>
<version>6.21.0</version>
</dependency>
案例:生成PDF报表
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
public class JasperReportExample {
public void generateReport(List<Employee> employees, String reportPath) {
try {
// 1. 编译jrxml文件(需先设计报表模板)
JasperReport jasperReport = JasperCompileManager
.compileReport("reports/employee_report.jrxml");
// 2. 准备数据源
JRBeanCollectionDataSource dataSource =
new JRBeanCollectionDataSource(employees);
// 3. 设置参数(可选)
JRParameter param = new JRParameter();
Map<String, Object> parameters = new HashMap<>();
parameters.put("TITLE", "员工信息报表");
parameters.put("COMPANY", "某科技有限公司");
// 4. 填充报表
JasperPrint jasperPrint = JasperFillManager
.fillReport(jasperReport, parameters, dataSource);
// 5. 导出为PDF
JasperExportManager.exportReportToPdfFile(jasperPrint, reportPath);
// 或导出为HTML
// JasperExportManager.exportReportToHtmlFile(jasperPrint, reportPath);
System.out.println("报表生成成功!");
} catch (JRException e) {
e.printStackTrace();
}
}
}
// 数据模型
class Employee {
private int id;
private String name;
private String department;
private double salary;
private LocalDate hireDate;
// getters and setters
}
报表模板设计 (employee_report.jrxml)
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports"
name="employee_report" pageWidth="595" pageHeight="842">
<parameter name="TITLE" class="java.lang.String"/>
<parameter name="COMPANY" class="java.lang.String"/>
<queryString>
<![CDATA[]]>
</queryString>
<field name="id" class="java.lang.Integer"/>
<field name="name" class="java.lang.String"/>
<field name="department" class="java.lang.String"/>
<field name="salary" class="java.lang.Double"/>
<band height="50">
<textField>
<reportElement x="0" y="0" width="515" height="30"/>
<textElement textAlignment="Center">
<font size="18" isBold="true"/>
</textElement>
<textFieldExpression>
<![CDATA[$P{TITLE}]]>
</textFieldExpression>
</textField>
</band>
</title>
<columnHeader>
<band height="20">
<staticText>
<reportElement x="0" y="0" width="100" height="20"/>
<text>ID</text>
</staticText>
<staticText>
<reportElement x="100" y="0" width="150" height="20"/>
<text>姓名</text>
</staticText>
<staticText>
<reportElement x="250" y="0" width="150" height="20"/>
<text>部门</text>
</staticText>
<staticText>
<reportElement x="400" y="0" width="115" height="20"/>
<text>薪资</text>
</staticText>
</band>
</columnHeader>
<detail>
<band height="20">
<textField>
<reportElement x="0" y="0" width="100" height="20"/>
<textFieldExpression>
<![CDATA[$F{id}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="100" y="0" width="150" height="20"/>
<textFieldExpression>
<![CDATA[$F{name}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="250" y="0" width="150" height="20"/>
<textFieldExpression>
<![CDATA[$F{department}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="400" y="0" width="115" height="20"/>
<textFieldExpression>
<![CDATA[$F{salary}]]>
</textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
使用EasyExcel(阿里巴巴优化版本)
依赖配置
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.4</version>
</dependency>
案例:大数据量导出
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
public class EasyExcelExample {
// 1. 定义导出模型
@Data
public static class OrderData {
@ExcelProperty("订单编号")
private String orderId;
@ExcelProperty("客户名称")
private String customerName;
@ExcelProperty("订单金额")
private BigDecimal amount;
@ExcelProperty("订单日期")
private Date orderDate;
@ExcelProperty("状态")
private String status;
}
// 2. 简单导出
public void simpleExport(List<OrderData> dataList, String fileName) {
EasyExcel.write(fileName, OrderData.class)
.sheet("订单列表")
.doWrite(dataList);
}
// 3. 复杂样式导出
public void styledExport(List<OrderData> dataList, String fileName) {
// 自定义样式
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
// 设置策略
WriteHandler writeHandler = new AbstractVerticalWriteHandler() {
// 自定义处理逻辑
};
EasyExcel.write(fileName, OrderData.class)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自适应列宽
.sheet("报表")
.doWrite(dataList);
}
// 4. Web下载
public void exportToWeb(HttpServletResponse response, List<OrderData> dataList) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition",
"attachment;filename=" + URLEncoder.encode("订单报表.xlsx", "UTF-8"));
try {
EasyExcel.write(response.getOutputStream(), OrderData.class)
.sheet("报表")
.doWrite(dataList);
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用iText生成PDF报表
依赖配置
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.2.5</version>
</dependency>
案例:生成发票PDF
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.property.UnitValue;
public class PDFInvoiceExample {
public void generateInvoice(Invoice invoice, String filePath) {
try {
PdfWriter writer = new PdfWriter(filePath);
PdfDocument pdf = new PdfDocument(writer);
Document document = new Document(pdf);
// 添加标题
document.add(new Paragraph("发票")
.setFontSize(20)
.setBold()
.setTextAlignment(TextAlignment.CENTER));
// 添加客户信息
document.add(new Paragraph("客户名称: " + invoice.getCustomerName()));
document.add(new Paragraph("发票日期: " + invoice.getDate()));
document.add(new Paragraph("发票编号: " + invoice.getInvoiceNo()));
// 创建表格
float[] columnWidths = {2, 1, 1, 1};
Table table = new Table(UnitValue.createPercentArray(columnWidths));
// 添加表头
table.addHeaderCell("商品名称");
table.addHeaderCell("数量");
table.addHeaderCell("单价");
table.addHeaderCell("金额");
// 添加数据行
for (InvoiceItem item : invoice.getItems()) {
table.addCell(item.getProductName());
table.addCell(String.valueOf(item.getQuantity()));
table.addCell(String.valueOf(item.getUnitPrice()));
table.addCell(String.valueOf(item.getAmount()));
}
document.add(table);
// 添加总计
document.add(new Paragraph("总计金额: " + invoice.getTotalAmount())
.setBold()
.setTextAlignment(TextAlignment.RIGHT));
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用DynamicReports(JasperReports的简化版)
依赖配置
<dependency>
<groupId>net.sourceforge.dynamicreports</groupId>
<artifactId>dynamicreports-core</artifactId>
<version>6.12.1</version>
</dependency>
案例:简洁报表生成
import static net.sf.dynamicreports.report.builder.DynamicReports.*;
public class DynamicReportsExample {
public void generateDynamicReport() {
try {
// 创建报表
JasperReportBuilder report = report()
.title(cmp.text("员工报表").setStyle(stl.style().bold()))
.columns(
col.column("ID", "id", type.integerType()),
col.column("姓名", "name", type.stringType()),
col.column("部门", "department", type.stringType()),
col.column("薪资", "salary", type.bigDecimalType())
)
.title(cmp.text("公司信息").setStyle(stl.style().bold()))
.pageFooter(cmp.pageXofY());
// 填充数据
List<Employee> employees = getEmployeeData();
report.setDataSource(employees);
// 导出为PDF
report.toPdf(new FileOutputStream("dynamic_report.pdf"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
技术选型建议
| 技术方案 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| Apache POI | 简单Excel导出 | 轻量级,灵活 | 代码量较大 |
| JasperReports | 复杂专业报表 | 功能强大,支持多种格式 | 学习曲线陡峭 |
| EasyExcel | 大数据量导出 | 内存优化好,API简洁 | 功能相对有限 |
| iText | PDF生成 | 专业的PDF处理 | 学习成本较高 |
| DynamicReports | 快速开发 | API简洁,易上手 | 功能不如JasperReports全面 |
最佳实践建议
- 项目初期:使用EasyExcel快速实现简单的Excel导出
- 复杂报表需求:采用JasperReports + 可视化设计器
- 大数据量场景:使用分页导出 + EasyExcel的流式写入
- PDF报表:考虑iText或JasperReports
- Web报表:可以引入ECharts等前端图表库实现可视化
选择方案时需考虑:数据量大小、报表复杂度、团队技术栈、维护成本等因素。