JasperReports - 以编程方式创建报告
最后修改于 2020 年 7 月 13 日
在本教程中,我们将展示如何以编程方式使用 JasperReports 创建报告。我们从 CARS 表创建一个 PDF 报告。我们在应用程序中使用 Derby 数据库。
一份报告是包含以叙述、图形或表格形式组织的信息的文档,该文档是根据临时、定期、重复、规则或按需编制的。JasperReports 是一个开源报告库。它从数据源加载数据并从中创建报告。报告是 PDF、HTML、RTF、XLS、ODT、CSV 或 XML 格式的文件。
JasperReports 使用模板来定义文档结构。模板在 XML 文件中定义,或使用编程 API 创建。在本教程中,我们使用后者选项。
-- SQL for the CARS table
CREATE TABLE CARS(ID BIGINT NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY
(START WITH 1, INCREMENT BY 1), NAME VARCHAR(30), PRICE INT);
INSERT INTO CARS(Name, Price) VALUES('Audi', 52642);
INSERT INTO CARS(Name, Price) VALUES('Mercedes', 57127);
INSERT INTO CARS(Name, Price) VALUES('Skoda', 9000);
INSERT INTO CARS(Name, Price) VALUES('Volvo', 29000);
INSERT INTO CARS(Name, Price) VALUES('Bentley', 350000);
INSERT INTO CARS(Name, Price) VALUES('Citroen', 21000);
INSERT INTO CARS(Name, Price) VALUES('Hummer', 41400);
INSERT INTO CARS(Name, Price) VALUES('Volkswagen', 21600);
在代码示例中,我们使用此表。
在 Derby 中创建 testdb 数据库
Derby 可以使用命令行工具或 NetBeans IDE 的 Services 选项卡进行管理。您可以在 Apache Derby 教程中找到更多关于 Derby 的信息。
$ export DERBY_HOME=$JAVA_HOME/db
我们设置了 DERBY_HOME 目录。Derby 附带 JDK,因此我们无需单独安装。
$ export DERBY_OPTS=-Dderby.system.home=/home/janbodnar/.derby
我们设置了 derby 系统目录。数据库和 derby 日志在此目录中创建。
$ $DERBY_HOME/bin/ij
我们启动了 ij 系统工具。
ij> CONNECT 'jdbc:derby:testdb;create=true';
使用 CONNECT 命令,我们创建了 testdb 数据库。
ij> run 'cars.sql';
使用 run 命令,我们执行 cars.sql 文件中的 SQL 语句。
ij> SELECT * FROM CARS; ID |NAME |PRICE --------------------------------------------------------------- 1 |Audi |52642 2 |Mercedes |57127 3 |Skoda |9000 4 |Volvo |29000 5 |Bentley |350000 6 |Citroen |21000 7 |Hummer |41400 8 |Volkswagen |21600
这是创建的 CARS 表。
ij> exit;
我们退出了 ij 工具。
$ $DERBY_HOME/bin/startNetworkServer &
我们启动了 Derby 网络服务器。
应用程序
以下应用程序连接到 Derby 服务器,从 CARS 表加载数据,并使用 JasperReports 从数据创建 PDF 文件。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ └── main
│ │ ├── CommandLineRunner.java
│ │ ├── JasperProgrammatic.java
│ │ └── ReportBuilder.java
│ └── resources
└── test
└── java
这是项目结构。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zetcode</groupId>
<artifactId>JasperProgrammatic3</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.13.1.1</version>
</dependency>
</dependencies>
</project>
在 Maven pom.xml 文件中,我们具有 jasperreports 和 derbyclient 依赖项。
package com.zetcode.main;
public class CommandLineRunner {
public static void main(String[] args) throws Exception {
JasperProgrammatic app = new JasperProgrammatic();
app.start();
}
}
CommandLineRunner 设置应用程序并调用其 start 方法。
package com.zetcode.main;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.design.JRDesignBand;
import net.sf.jasperreports.engine.design.JRDesignExpression;
import net.sf.jasperreports.engine.design.JRDesignField;
import net.sf.jasperreports.engine.design.JRDesignParameter;
import net.sf.jasperreports.engine.design.JRDesignQuery;
import net.sf.jasperreports.engine.design.JRDesignSection;
import net.sf.jasperreports.engine.design.JRDesignStaticText;
import net.sf.jasperreports.engine.design.JRDesignStyle;
import net.sf.jasperreports.engine.design.JRDesignTextField;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.type.HorizontalTextAlignEnum;
public class ReportBuilder {
public JasperDesign build() throws JRException {
JasperDesign jasDes = new JasperDesign();
jasDes.setName("myreport");
jasDes.setPageWidth(595);
jasDes.setPageHeight(842);
jasDes.setLeftMargin(20);
jasDes.setRightMargin(20);
jasDes.setTopMargin(20);
jasDes.setBottomMargin(20);
jasDes.setColumnWidth(555);
// Style
JRDesignStyle mystyle = new JRDesignStyle();
mystyle.setName("mystyle");
mystyle.setDefault(true);
mystyle.setFontName("DejaVu Sans");
mystyle.setFontSize(22f);
mystyle.setPdfFontName("Helvetica");
mystyle.setPdfEncoding("UTF-8");
jasDes.addStyle(mystyle);
// Fields
JRDesignField field1 = new JRDesignField();
field1.setName("id");
field1.setValueClass(String.class);
jasDes.addField(field1);
JRDesignField field2 = new JRDesignField();
field2.setName("name");
field2.setValueClass(String.class);
jasDes.addField(field2);
JRDesignField field3 = new JRDesignField();
field3.setName("price");
field3.setValueClass(String.class);
jasDes.addField(field3);
// Parameter
JRDesignParameter par = new JRDesignParameter();
par.setName("CarPrice");
par.setValueClass(Integer.class);
jasDes.addParameter(par);
// Query
JRDesignQuery query = new JRDesignQuery();
query.setText("SELECT * FROM Cars WHERE Price > $P{CarPrice}");
jasDes.setQuery(query);
// Title
JRDesignBand titleBand = new JRDesignBand();
titleBand.setHeight(50);
JRDesignStaticText titleText = new JRDesignStaticText();
titleText.setText("Expensive cars");
titleText.setX(0);
titleText.setY(10);
titleText.setWidth(515);
titleText.setHeight(30);
titleText.setHorizontalTextAlign(HorizontalTextAlignEnum.CENTER);
titleText.setFontSize(22f);
titleBand.addElement(titleText);
jasDes.setTitle(titleBand);
// Detail
JRDesignBand detailBand = new JRDesignBand();
detailBand.setHeight(60);
JRDesignTextField tf1 = new JRDesignTextField();
tf1.setBlankWhenNull(true);
tf1.setX(0);
tf1.setY(10);
tf1.setWidth(60);
tf1.setHeight(30);
tf1.setHorizontalTextAlign(HorizontalTextAlignEnum.CENTER);
tf1.setStyle(mystyle);
tf1.setExpression(new JRDesignExpression("$F{id}"));
detailBand.addElement(tf1);
JRDesignTextField tf2 = new JRDesignTextField();
tf2.setBlankWhenNull(true);
tf2.setX(80);
tf2.setY(10);
tf2.setWidth(120);
tf2.setHeight(30);
tf2.setHorizontalTextAlign(HorizontalTextAlignEnum.LEFT);
tf2.setStyle(mystyle);
tf2.setExpression(new JRDesignExpression("$F{name}"));
detailBand.addElement(tf2);
JRDesignTextField tf3 = new JRDesignTextField();
tf3.setBlankWhenNull(true);
tf3.setX(200);
tf3.setY(10);
tf3.setWidth(100);
tf3.setHeight(30);
tf3.setHorizontalTextAlign(HorizontalTextAlignEnum.RIGHT);
tf3.setStyle(mystyle);
tf3.setExpression(new JRDesignExpression("$F{price}"));
detailBand.addElement(tf3);
((JRDesignSection) jasDes.getDetailSection()).addBand(detailBand);
return jasDes;
}
}
在 ReportBuilder 类中,我们以编程方式创建 Jasper 报告。
JasperDesign jasDes = new JasperDesign();
jasDes.setName("myreport");
jasDes.setPageWidth(595);
jasDes.setPageHeight(842);
jasDes.setLeftMargin(20);
jasDes.setRightMargin(20);
jasDes.setTopMargin(20);
jasDes.setBottomMargin(20);
jasDes.setColumnWidth(555);
JasperDesign 在使用编程 API 创建报告方面发挥着重要作用。通过其 setter 方法,我们定义了报告名称及其基本设置。
// Style
JRDesignStyle mystyle = new JRDesignStyle();
mystyle.setName("mystyle");
mystyle.setDefault(true);
mystyle.setFontName("DejaVu Sans");
mystyle.setFontSize(22f);
mystyle.setPdfFontName("Helvetica");
mystyle.setPdfEncoding("UTF-8");
jasDes.addStyle(mystyle);
JRDesignStyle 用于定义自定义样式。使用样式可避免代码重复。
// Fields
JRDesignField field1 = new JRDesignField();
field1.setName("id");
field1.setValueClass(String.class);
jasDes.addField(field1);
JRDesignField field2 = new JRDesignField();
field2.setName("name");
field2.setValueClass(String.class);
jasDes.addField(field2);
JRDesignField field3 = new JRDesignField();
field3.setName("price");
field3.setValueClass(String.class);
jasDes.addField(field3);
我们有三个字段,分别对应 CARS 表的三个列。报告字段是元素,表示数据源和报告模板之间的数据映射。
// Parameter
JRDesignParameter par = new JRDesignParameter();
par.setName("CarPrice");
par.setValueClass(Integer.class);
jasDes.addParameter(par);
JRDesignParameter 定义了传递给报告引擎的报告填充操作的动态参数。
// Query
JRDesignQuery query = new JRDesignQuery();
query.setText("SELECT * FROM Cars WHERE Price > $P{CarPrice}");
jasDes.setQuery(query);
JRDesignQuery 定义了从数据库获取数据的 SQL 查询。$P{CarPrice} 占位符由传递给报告引擎的参数值填充。
// Title
JRDesignBand titleBand = new JRDesignBand();
titleBand.setHeight(50);
JRDesignStaticText titleText = new JRDesignStaticText();
titleText.setText("Expensive cars");
titleText.setX(0);
titleText.setY(10);
titleText.setWidth(515);
titleText.setHeight(30);
titleText.setHorizontalTextAlign(HorizontalTextAlignEnum.CENTER);
titleText.setFontSize(22f);
titleBand.addElement(titleText);
jasDes.setTitle(titleBand);
在这里,我们创建了一个报告标题。标题是 JRDesignStaticText 的实例。报告模板由几个部分组成,包括标题、列标题、详细信息和摘要。每个部分都放置在一个称为“band”的水平区域中。
// Detail
JRDesignBand detailBand = new JRDesignBand();
detailBand.setHeight(60);
JRDesignTextField tf1 = new JRDesignTextField();
tf1.setBlankWhenNull(true);
tf1.setX(0);
tf1.setY(10);
tf1.setWidth(60);
tf1.setHeight(30);
tf1.setHorizontalTextAlign(HorizontalTextAlignEnum.CENTER);
tf1.setStyle(mystyle);
tf1.setExpression(new JRDesignExpression("$F{id}"));
detailBand.addElement(tf1);
...
Detail 是放置报告数据的 band。表中的每一行都由 detail band 中的一行表示。detail band 由三个 JRDesignTextFields 组成,这些字段会根据给定的表达式生成动态数据。$F{id} 表达式给出当前表行的 ID。
package com.zetcode.main;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JasperDesign;
public class JasperProgrammatic {
private Connection con;
public void start() throws JRException, SQLException {
try {
String url = "jdbc:derby://:1527/testdb";
String user = "app";
String password = "app";
DriverManager.registerDriver(new org.apache.derby.jdbc.ClientDriver());
con = DriverManager.getConnection(url, user, password);
JasperDesign jdes = new ReportBuilder().build();
JasperReport report = JasperCompileManager.compileReport(jdes);
HashMap params = new HashMap();
params.put("CarPrice", 30000);
JasperPrint jprint = JasperFillManager.fillReport(report, params, con);
JasperExportManager.exportReportToPdfFile(jprint,
"src/main/resources/expensivecars.pdf");
} catch (SQLException ex) {
Logger.getLogger(JasperProgrammatic.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (con != null) {
con.close();
}
}
}
}
在 JasperProgrammatic 类中,我们构建、编译和导出报告。
JasperDesign jdes = new ReportBuilder().build();
使用 ReportBuilder 类创建 JasperDesign,这是一个由编程 API 创建的报告模板。
JasperReport report = JasperCompileManager.compileReport(jdes);
使用 JasperCompileManager,我们将模板编译成一个中间的 JasperReport 对象。
HashMap params = new HashMap();
params.put("CarPrice", 30000);
我们的参数映射包含一个参数:CarPrice,用于过滤数据。报告中只显示昂贵的汽车。
JasperPrint jprint = JasperFillManager.fillReport(report, params, con);
下一步是用数据填充已编译的 JasperReport 对象。此操作会生成一个 JasperPrint 对象,该对象可以查看、打印或导出为其他格式。
JasperExportManager.exportReportToPdfFile(jprint,
"src/main/resources/expensivecars.pdf");
我们使用 JasperExportManager 将 JasperPrint 对象导出为 PDF 文件。生成的 PDF 文件写入 src/main/resources/ 目录。
在本教程中,我们使用 JasperReports 库的编程 API 创建了一个 PDF 文件报告。