2. Maven依赖

首先需要在pom.xml中添加jasperreports依赖:

<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports</artifactId>
    <version>6.20.0</version>
</dependency>

最新版本可在Maven仓库查询。

3. 报表模板

报表设计通过JRXML文件定义,这是JasperReports引擎可解析的特殊XML结构。我们重点关注与Java代码生成相关的核心结构。

以下是一个员工信息报表的简化示例:

<jasperReport ... >
    <field name="FIRST_NAME" class="java.lang.String"/>
    <field name="LAST_NAME" class="java.lang.String"/>
    <field name="SALARY" class="java.lang.Double"/>
    <field name="ID" class="java.lang.Integer"/>
    <detail>
        <band height="51" splitType="Stretch">
            <textField>
                <reportElement x="0" y="0" width="100" height="20"/>
                <textElement/>
                <textFieldExpression class="java.lang.String">
                  <![CDATA[$F{FIRST_NAME}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="100" y="0" width="100" height="20"/>
                <textElement/>
                <textFieldExpression class="java.lang.String">
                  <![CDATA[$F{LAST_NAME}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="200" y="0" width="100" height="20"/>
                <textElement/>
                <textFieldExpression class="java.lang.String">
                  <![CDATA[$F{SALARY}]]></textFieldExpression>
            </textField>
        </band>
    </detail>
</jasperReport>

3.1. 编译报表

JRXML文件需先编译才能填充数据。使用JasperCompileManager完成编译:

InputStream employeeReportStream
  = getClass().getResourceAsStream("/employeeReport.jrxml");
JasperReport jasperReport
  = JasperCompileManager.compileReport(employeeReportStream);

为避免重复编译,可保存为.jasper文件:

JRSaver.saveObject(jasperReport, "employeeReport.jasper");

4. 填充报表

最常用方式是通过数据库记录填充报表,需在报表中定义SQL查询。先修改报表添加查询语句:

<jasperReport ... >
    <queryString>
        <![CDATA[SELECT * FROM EMPLOYEE]]>
    </queryString>
    ...
</jasperReport>

创建数据源:

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
      .setType(EmbeddedDatabaseType.HSQL)
      .addScript("classpath:employee-schema.sql")
      .build();
}

填充报表:

JasperPrint jasperPrint = JasperFillManager.fillReport(
  jasperReport, null, dataSource.getConnection());

⚠️ 第二个参数传null,因为报表暂未使用参数。

4.1. 参数使用

参数适用于传递数据源中不存在或需动态调整的数据。可修改SQL查询或报表内容。先添加三个参数:

<jasperReport ... >
    <parameter name="title" class="java.lang.String" />
    <parameter name="minSalary" class="java.lang.Double" />
    <parameter name="condition" class="java.lang.String">
        <defaultValueExpression>
          <![CDATA["1 = 1"]]></defaultValueExpression>
    </parameter>
    // ...
</jasperreport>

添加标题段显示title参数:

<jasperreport ... >
    // ...
    <title>
        <band height="20" splitType="Stretch">
            <textField>
                <reportElement x="238" y="0" width="100" height="20"/>
                <textElement/>
                <textFieldExpression class="java.lang.String">
                  <![CDATA[$P{title}]]></textFieldExpression>
            </textField>
        </band>
    </title>
    ...
</jasperreport/>

修改查询使用参数:

SELECT * FROM EMPLOYEE
  WHERE SALARY >= $P{minSalary} AND $P!{condition}

✅ 注意condition参数使用$P!{}语法,表示直接拼接SQL而非预编译参数。

准备参数并填充报表:

Map<String, Object> parameters = new HashMap<>();
parameters.put("title", "Employee Report");
parameters.put("minSalary", 15000.0);
parameters.put("condition", " LAST_NAME ='Smith' ORDER BY FIRST_NAME");

JasperPrint jasperPrint
  = JasperFillManager.fillReport(..., parameters, ...);

❌ 参数名需与报表定义一致,缺失参数将使用defaultValueExpression默认值。

5. 导出报表

导出步骤:

  1. 创建对应格式的Exporter对象
  2. 设置填充后的报表作为输入
  3. 指定输出位置
  4. 可选配置导出参数

5.1. PDF导出

JRPdfExporter exporter = new JRPdfExporter();

exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
exporter.setExporterOutput(
  new SimpleOutputStreamExporterOutput("employeeReport.pdf"));

SimplePdfReportConfiguration reportConfig
  = new SimplePdfReportConfiguration();
reportConfig.setSizePageToContent(true);
reportConfig.setForceLineBreakPolicy(false);

SimplePdfExporterConfiguration exportConfig
  = new SimplePdfExporterConfiguration();
exportConfig.setMetadataAuthor("baeldung");
exportConfig.setEncrypted(true);
exportConfig.setAllowedPermissionsHint("PRINTING");

exporter.setConfiguration(reportConfig);
exporter.setConfiguration(exportConfig);

exporter.exportReport();

5.2. XLS导出

JRXlsxExporter exporter = new JRXlsxExporter();
 
// 设置输入输出...
SimpleXlsxReportConfiguration reportConfig
  = new SimpleXlsxReportConfiguration();
reportConfig.setSheetNames(new String[] { "Employee Data" });

exporter.setConfiguration(reportConfig);
exporter.exportReport();

5.3. CSV导出

JRCsvExporter exporter = new JRCsvExporter();
 
// 设置输入...
exporter.setExporterOutput(
  new SimpleWriterExporterOutput("employeeReport.csv"));

exporter.exportReport();

5.4. HTML导出

HtmlExporter exporter = new HtmlExporter();
 
// 设置输入...
exporter.setExporterOutput(
  new SimpleHtmlExporterOutput("employeeReport.html"));

exporter.exportReport();

6. 子报表

子报表本质是嵌入到主报表中的标准报表。先创建员工邮箱子报表:

<jasperReport ... >
    <parameter name="idEmployee" class="java.lang.Integer" />
    <queryString>
        <![CDATA[SELECT * FROM EMAIL WHERE ID_EMPLOYEE = $P{idEmployee}]]>
    </queryString>
    <field name="ADDRESS" class="java.lang.String"/>
    <detail>
        <band height="20" splitType="Stretch">
            <textField>
                <reportElement x="0" y="0" width="156" height="20"/>
                <textElement/>
                <textFieldExpression class="java.lang.String">
                  <![CDATA[$F{ADDRESS}]]></textFieldExpression>
            </textField>
        </band>
    </detail>
</jasperReport>

修改主报表嵌入子报表:

<detail>
    <band ... >
        <subreport>
            <reportElement x="0" y="20" width="300" height="27"/>
            <subreportParameter name="idEmployee">
                <subreportParameterExpression>
                  <![CDATA[$F{ID}]]></subreportParameterExpression>
            </subreportParameter>
            <connectionExpression>
              <![CDATA[$P{REPORT_CONNECTION}]]></connectionExpression>
            <subreportExpression class="java.lang.String">
              <![CDATA["employeeEmailReport.jasper"]]></subreportExpression>
        </subreport>
    </band>
</detail>

✅ 通过编译后的.jasper文件引用子报表,并传递idEmployee参数和数据库连接。

编译两个报表:

InputStream employeeReportStream
  = getClass().getResourceAsStream("/employeeReport.jrxml");
JasperReport jasperReport
  = JasperCompileManager.compileReport(employeeReportStream);
JRSaver.saveObject(jasperReport, "employeeReport.jasper");

InputStream emailReportStream
  = getClass().getResourceAsStream("/employeeEmailReport.jrxml");
JRSaver.saveObject(
  JasperCompileManager.compileReport(emailReportStream),
  "employeeEmailReport.jasper");

填充和导出代码无需修改。

7. 总结

本文简要介绍了JasperReports核心功能:

  • 编译报表并填充数据库记录
  • 通过参数动态控制报表内容
  • 嵌入子报表
  • 导出多种格式(PDF/XLS/CSV/HTML)

完整代码示例见GitHub仓库


原始标题:JasperReports with Spring