杰瑞科技汇

jasperreports教程

JasperReports 完整教程

目录

  1. 第一部分:初识 JasperReports

    jasperreports教程-图1
    (图片来源网络,侵删)
    • 1 什么是 JasperReports?
    • 2 核心概念
    • 3 为什么选择 JasperReports?
    • 4 环境准备
  2. 第二部分:你的第一个 JasperReport 报表

    • 1 设计报表模板(.jrxml)
    • 2 编译模板(.jasper)
    • 3 填充数据(.jrprint)
    • 4 导出报表
  3. 第三部分:报表设计详解

    • 1 JasperReports 结构
    • 2 常用设计元素
    • 3 数据源
    • 4 参数
    • 5 变量
  4. 第四部分:进阶功能

    • 1 子报表
    • 2 分组与排序
    • 3 图表
    • 4 条件样式
    • 5 脚本
  5. 第五部分:实战案例

    jasperreports教程-图2
    (图片来源网络,侵删)
    • 1 案例一:从数据库生成简单列表报表
    • 2 案例二:带分组的销售报表
    • 3 案例三:使用子报表的订单详情报表
  6. 第六部分:集成与部署

    • 1 在 Java Web 应用中集成
    • 2 在 Spring Boot 中集成
  7. 第七部分:学习资源与最佳实践


第一部分:初识 JasperReports

1 什么是 JasperReports?

JasperReports 是一个功能强大的开源 Java 报表工具,它允许开发者以 XML 格式(.jrxml)设计报表模板,然后通过 Java 代码将数据填充到模板中,最终生成各种格式的报表,如 PDF、HTML、Excel、CSV 等。

它的核心思想是 “模板 + 数据 = 报表”

jasperreports教程-图3
(图片来源网络,侵删)

2 核心概念

  • Report Template (报表模板): 一个 XML 文件(.jrxml),定义了报表的布局、样式、字段位置等,这是报表的“骨架”。
  • Data Source (数据源): 提供报表所需数据的来源,可以是 Java 对象数组、java.sql.ConnectionJRBeanCollectionDataSourceCSV 文件等。
  • Parameters (参数): 传递给报表的值,用于报表的标题、过滤条件或作为子报表的连接参数,它们在报表执行时是只读的。
  • Fields (字段): 代表数据源中每一条记录的单个数据项,如果数据源是 User 对象列表,fields username, email 等。
  • Variables (变量): JasperReports 在报表生成过程中计算出的值。COUNT(计数)、SUM(求和)、TOTAL_PAGE(总页数)等。
  • Compiled Report (编译后的报表): 将 .jrxml 文件编译后生成的二进制文件(.jasper),这个文件包含了报表的所有设计信息和格式指令,可以被 JasperReports 引擎直接使用。

3 为什么选择 JasperReports?

  • 功能强大: 支持复杂报表、交叉表、图表、子报表等。
  • 格式多样: 可导出为 PDF、HTML、Excel、XML、CSV、ODT 等多种格式。
  • 开源免费: 无需支付任何费用。
  • 跨平台: 基于 Java,可在任何支持 Java 的平台上运行。
  • 灵活: 可以轻松集成到任何 Java 应用程序(桌面、Web、移动)中。

4 环境准备

  1. JDK: 安装 JDK 8 或更高版本。
  2. IDE: 推荐使用 IntelliJ IDEA 或 Eclipse。
  3. JasperReports 库: 从 JasperReports 官网 下载最新的 JAR 包,或在 Maven 项目中添加依赖。
    <!-- Maven 依赖 -->
    <dependency>
        <groupId>net.sf.jasperreports</groupId>
        <artifactId>jasperreports</artifactId>
        <version>6.20.6</version> <!-- 请使用最新版本 -->
    </dependency>

第二部分:你的第一个 JasperReport 报表

我们将创建一个简单的 "Hello World" 报表,它不依赖数据库,而是使用硬编码数据。

步骤 1: 设计报表模板 (.jrxml)

创建一个名为 report_template.jrxml 的文件,这是一个标准的 XML 文件。

<?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 http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="MyFirstReport" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
        <band height="50">
            <staticText>
                <reportElement x="0" y="0" width="555" height="50"/>
                <textElement textAlignment="Center">
                    <font size="24" isBold="true"/>
                </textElement>
                <text><![CDATA[Hello JasperReports!]]></text>
            </staticText>
        </band>
    </title>
    <detail>
        <band height="100">
            <staticText>
                <reportElement x="0" y="0" width="200" height="30"/>
                <textElement>
                    <font size="16"/>
                </textElement>
                <text><![CDATA[这是我的第一个报表,]]></text>
            </staticText>
        </band>
    </detail>
</jasperReport>

模板解释:

  • <jasperReport>: 根元素,定义报表的基本属性(页面大小、边距等)。
  • <title>: 报表的标题带,通常出现在第一页的顶部。
  • <band>: 报表中的一个带(或区域),有固定的高度。
  • <staticText>: 静态文本,内容在编译时确定。
  • <detail>: 报表的细节带,对于数据源中的每一条记录,这里的内容都会被渲染一次。

步骤 2: 编译模板

使用 Java 代码编译 .jrxml 文件,生成 .jasper 文件。

import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperReport;
import java.io.File;
import java.io.InputStream;
public class CompileReport {
    public static void main(String[] args) {
        try {
            // 加载 jrxml 文件(从 classpath)
            InputStream jrxmlStream = CompileReport.class.getResourceAsStream("/report_template.jrxml");
            // 编译 jrxml 文件,生成 jasper 文件
            JasperReport jasperReport = JasperCompileManager.compileReport(jrxmlStream);
            // (可选)将编译后的 jasper 文件保存到磁盘
            JasperCompileManager.writeReportToFile("C:/temp/MyFirstReport.jasper", jasperReport);
            System.out.println("报表编译成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

步骤 3: 填充数据

由于我们的报表没有使用数据源,这一步很简单,我们只需要创建一个空的 JasperPrint 对象。

import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FillReport {
    public static void main(String[] args) {
        try {
            // 1. 加载编译后的报表文件
            JasperReport jasperReport = (JasperReport) JRLoader.loadObject(new File("C:/temp/MyFirstReport.jasper"));
            // 2. 准备参数 (Map)
            Map<String, Object> parameters = new HashMap<>();
            // 3. 准备数据源 (本例为空)
            // JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(getData());
            // 因为我们的报表没有字段,所以可以传 null
            // 4. 填充报表
            JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, null);
            System.out.println("报表填充成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 下一步是导出...
    }
    // 一个示例数据源方法,用于后续例子
    private static List<User> getData() {
        List<User> users = new ArrayList<>();
        users.add(new User("张三", 25, "北京"));
        users.add(new User("李四", 30, "上海"));
        users.add(new User("王五", 28, "广州"));
        return users;
    }
}
// POJO 类
class User {
    private String name;
    private Integer age;
    private String city;
    // 构造器、Getter 和 Setter
    public User(String name, Integer age, String city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }
    public String getName() { return name; }
    public Integer getAge() { return age; }
    public String getCity() { return city; }
}

步骤 4: 导出报表

将填充好的 JasperPrint 对象导出为 PDF 文件。

import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperPrint;
import java.io.File;
public class ExportReport {
    public static void main(String[] args) {
        try {
            // 假设 jasperPrint 已经在 FillReport 步骤中生成
            JasperPrint jasperPrint = FillReport.getJasperPrint(); // 需要修改 FillReport 使其返回 jasperPrint
            // 导出为 PDF
            JasperExportManager.exportReportToPdfFile(jasperPrint, "C:/temp/MyFirstReport.pdf");
            System.out.println("报表导出成功!请查看 C:/temp/MyFirstReport.pdf");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

将以上四个步骤的代码整合起来,你就成功生成了你的第一个 JasperReports PDF 报表!


第三部分:报表设计详解

1 JasperReports 结构

一个典型的 .jrxml 文件结构如下:

<jasperReport ...>
    <import>...</import> <!-- Java 类导入 -->
    <style>...</style>   <!-- 样式定义 -->
    <parameter>...</parameter> <!-- 参数定义 -->
    <field>...</field>   <!-- 字段定义 -->
    <variable>...</variable> <!-- 变量定义 -->
    <queryString>...</queryString> <!-- SQL 查询语句 (用于 JDBC 数据源) -->...</title>   <!-- 标题带 -->
    <pageHeader>...</pageHeader> <!-- 页眉带 -->
    <columnHeader>...</columnHeader> <!-- 列标题带 -->
    <detail>...</detail> <!-- 细节带 -->
    <columnFooter>...</columnFooter> <!-- 列脚带 -->
    <pageFooter>...</pageFooter> <!-- 页脚带 -->
    <summary>...</summary> <!-- 总结带 -->
</jasperReport>

2 常用设计元素

  • <staticText>: 静态文本。
  • <textField>: 动态文本,用于显示字段、参数或变量的值。
    • <textFieldExpression><![CDATA[$F{fieldName}]]></textFieldExpression>: 显示字段 fieldName 的值。
    • <textFieldExpression><![CDATA[$P{parameterName}]]></textFieldExpression>: 显示参数 parameterName 的值。
  • <image>: 显示图片。
  • <line>, <rectangle>, <ellipse>: 绘制图形元素。
  • <frame>: 一个容器,可以包含其他元素,方便布局。

3 数据源

JasperReports 支持多种数据源:

  1. JRBeanCollectionDataSource: 最常用的数据源之一,用于 Java 对象的集合。

    List<User> users = ...; // 你的数据列表
    JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(users);
  2. JDBCConnectionDataSource: 直接使用数据库连接。

    Connection conn = DriverManager.getConnection(url, user, password);
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, conn);
  3. JRBeanArrayDataSource: 用于 Java 对象数组。

  4. JRMapCollectionDataSource: 用于 Map 对象的集合,Map 的 key 对应报表的 field name。

4 参数

参数在报表设计时定义,在填充时传入。

.jrxml 中定义参数:

<parameter name="ReportTitle" class="java.lang.String">
    <defaultValueExpression><![CDATA["客户列表"]]></defaultValueExpression>
</parameter>

在 Java 中传入参数:

Map<String, Object> parameters = new HashMap<>();
parameters.put("ReportTitle", "年度客户报表");

在报表中使用参数:

<staticText>
    <reportElement x="0" y="0" width="200" height="30"/>
    <textElement/>
    <text><![CDATA[$P{ReportTitle}]]></text>
</staticText>

5 变量

变量用于在报表生成过程中进行计算。

.jrxml 中定义变量:

<variable name="TotalAge" class="java.lang.Integer" calculation="Sum">
    <variableExpression><![CDATA[$F{age}]]></variableExpression>
</variable>

变量计算类型:

  • Sum: 求和
  • Count: 计数
  • Average: 平均值
  • Highest: 最大值
  • Lowest: 最小值

在报表中使用变量:

<textField>
    <reportElement x="400" y="0" width="100" height="30"/>
    <textElement/>
    <textFieldExpression><![CDATA[$V{TotalAge}]]></textFieldExpression>
</textField>

第四部分:进阶功能

1 子报表

子报表是嵌入到主报表中的一个独立报表,常用于显示一对多的关系,例如一个订单(主报表)包含多个订单项(子报表)。

步骤:

  1. 分别设计主报表模板(.jrxml)和子报表模板(.jrxml),并编译它们。
  2. 在主报表中,使用 <subreport> 元素。
  3. 使用参数传递数据给子报表。

主报表中的 <subreport> 元素:

<subreport>
    <reportElement x="0" y="50" width="500" height="100"/>
    <!-- 指定子报表的 .jasper 文件路径 -->
    <subreportExpression><![CDATA["C:/temp/OrderItems.jasper"]]></subreportExpression>
    <!-- 传递参数给子报表 -->
    <subreportParameterExpression>
        <map>
            <entry key="OrderId" value="$F{orderId}"/>
        </map>
    </subreportParameterExpression>
</subreport>

2 分组与排序

使用 <group> 元素可以对数据进行分组。

<group name="GroupByCity">
    <groupExpression><![CDATA[$F{city}]]></groupExpression> <!-- 分组表达式 -->
    <groupHeader>
        <band height="30">
            <staticText>
                <reportElement x="0" y="0" width="100" height="30"/>
                <textElement/>
                <text><![CDATA["城市: " + $V{GROUP_NAME}]]></text> <!-- GROUP_NAME 是内置变量 -->
            </staticText>
        </band>
    </groupHeader>
    <groupFooter>
        <band height="20">
            <!-- 可以在这里放分组的总结信息,如小计 -->
        </band>
    </groupFooter>
</group>

3 图表

JasperReports 内置了对 JFreeChart 的支持,可以轻松添加图表。

  1. .jrxml 中添加 <chart> 元素。
  2. 使用 <pieChart>, <barChart>, <lineChart> 等定义图表类型。
  3. 使用 <categorySeries><pieSeries> 定义数据系列,数据源通常是报表的变量。

4 条件样式

可以使用 ConditionalStyle 为元素(如文本、单元格)设置动态样式。

<textField>
    <reportElement x="100" y="0" width="100" height="20"/>
    <textElement/>
    <textFieldExpression><![CDATA[$F{age}]]></textFieldExpression>
    <!-- 条件样式 -->
    <conditionalStyle>
        <conditionExpression><![CDATA[$F{age} > 30]]></conditionExpression>
        <style mode="Opaque" backcolor="#FFCCCC"/> <!-- 年龄大于30岁时,背景变红 -->
    </conditionalStyle>
</textField>

5 脚本

JasperReports 支持 Java 代码片段,可以在报表生命周期的特定时刻执行(如 reportcolumnbandelement 级别)。

<report ... scriptletClass="com.example.MyScriptlet">
    ...
</report>

MyScriptlet.java:

import net.sf.jasperreports.engine.JRDefaultScriptlet;
import net.sf.jasperreports.engine.JRScriptlet;
public class MyScriptlet extends JRDefaultScriptlet {
    @Override
    public void afterGroupInit(String groupName) {
        // 在组初始化后执行
        System.out.println("Group " + groupName + " initialized.");
    }
}

第五部分:实战案例

从数据库生成简单列表报表

目标: 从数据库查询用户列表,并生成 PDF 报表。

  1. 创建 users:

    CREATE TABLE users (
        id INT PRIMARY KEY,
        name VARCHAR(100),
        email VARCHAR(100),
        registration_date DATE
    );
  2. 设计 UserList.jrxml:

    • 定义字段: id, name, email, registration_date
    • <detail> 带中使用 <textField> 显示这些字段。
  3. 编写 Java 代码:

    import net.sf.jasperreports.engine.*;
    import net.sf.jasperreports.engine.util.JRLoader;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.util.HashMap;
    import java.util.Map;
    public class DatabaseReport {
        public static void main(String[] args) {
            String jdbcUrl = "jdbc:mysql://localhost:3306/your_database";
            String user = "your_username";
            String password = "your_password";
            String jrxmlPath = "UserList.jrxml";
            String jasperPath = "UserList.jasper";
            String pdfPath = "UserList.pdf";
            try (Connection conn = DriverManager.getConnection(jdbcUrl, user, password)) {
                // 1. 编译 jrxml
                JasperReport jasperReport = JasperCompileManager.compileReport(jrxmlPath);
                // 2. 准备参数
                Map<String, Object> parameters = new HashMap<>();
                // 3. 填充报表 (直接使用 Connection)
                JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, conn);
                // 4. 导出 PDF
                JasperExportManager.exportReportToPdfFile(jasperPrint, pdfPath);
                System.out.println("数据库报表生成成功!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

第六部分:集成与部署

1 在 Java Web 应用中集成

通常使用 JasperReportsservlet 来处理报表的显示和导出。

  1. 配置 web.xml:

    <servlet>
        <servlet-name>JasperReportServlet</servlet-name>
        <servlet-class>net.sf.jasperreports.j2ee.servlets.ImageServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>JasperReportServlet</servlet-name>
        <url-pattern>/report/image</url-pattern>
    </servlet-mapping>
  2. 创建 JSP 页面:

    • 一个表单,用于输入报表参数和选择导出格式。
    • 使用 <iframe><img> 标签来显示由另一个 servlet 生成的 HTML 或图片报表。
  3. 创建报表生成 Servlet:

    • 获取请求参数。
    • 连接数据库,填充报表。
    • 根据 format 参数(如 html, pdf)调用相应的导出方法,并将结果写入 response 的输出流中。

2 在 Spring Boot 中集成

Spring Boot 与 JasperReports 集成非常方便。

  1. 添加依赖:

    <dependency>
        <groupId>net.sf.jasperreports</groupId>
        <artifactId>jasperreports</artifactId>
        <version>6.20.6</version>
    </dependency>
  2. 创建报表工具类:

    @Service
    public class JasperReportService {
        public byte[] exportReport(String reportName, Map<String, Object> parameters, List<?> data) throws JRException {
            // 加载编译后的报表文件
            InputStream jasperStream = this.getClass().getResourceAsStream("/reports/" + reportName + ".jasper");
            JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(data);
            JasperPrint jasperPrint = JasperFillManager.fillReport(jasperStream, parameters, dataSource);
            return JasperExportManager.exportReportToPdf(jasperPrint);
        }
    }
  3. 创建 Controller:

    @RestController
    @RequestMapping("/api/reports")
    public class ReportController {
        @Autowired
        private JasperReportService reportService;
        @GetMapping(value = "/users", produces = MediaType.APPLICATION_PDF_VALUE)
        public ResponseEntity<byte[]> generateUserReport() {
            try {
                List<User> users = userService.findAllUsers(); // 从你的Service获取数据
                Map<String, Object> parameters = new HashMap<>();
                parameters.put("ReportTitle", "用户列表");
                byte[] pdfBytes = reportService.exportReport("UserList", parameters, users);
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_PDF);
                headers.setContentDisposition(ContentDisposition.builder("attachment").filename("user_list.pdf").build());
                return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK);
            } catch (Exception e) {
                return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
            }
        }
    }

第七部分:学习资源与最佳实践

学习资源

  • 官方文档: JasperReports 官方文档 (最权威,但英文)
  • 示例项目: 官方网站提供了大量 .jrxml 示例,是学习设计技巧的绝佳资源。
  • 社区: JasperSoft Community
  • 博客和教程: 搜索 "JasperReports tutorial" 可以找到很多中文和英文的博客文章和视频教程。

最佳实践

  1. 版本控制: 将 .jrxml 文件(而不是 .jasper 文件)纳入版本控制系统(如 Git),这样团队可以协作修改报表设计。
  2. 报表分离: 将报表模板文件(.jrxml)放在项目的 src/main/resources/reports 目录下,便于管理和部署。
  3. 使用 DTO (Data Transfer Object): 不要直接将 JPA/Hibernate 实体对象作为报表数据源,创建专门的 DTO 类,只包含报表需要的字段,这可以避免 LazyInitializationException 并提高性能。
  4. 缓存编译后的报表: 对于频繁使用的报表,缓存 .jasper 文件可以避免每次请求都重新编译,提升性能。
  5. 性能优化: 对于大数据量报表,考虑使用 SQL 查询进行数据预处理(如分组、求和),而不是在 JasperReports 变量中计算,以减轻 JVM 的压力。
  6. 模块化: 对于复杂的报表,尽量使用子报表、图表和参数来模块化设计,使主报表更清晰。

希望这份详细的教程能帮助你从入门到精通 JasperReports!祝你学习愉快!

分享:
扫描分享到社交APP
上一篇
下一篇