这篇文章,主要介绍如何使用Jasper Studio制作父子报表,并且通过Java + Parameters参数填充模板文件。
目录
一、JasperReports实现父子报表
1.1、运行效果
1.2、制作模板
(1)制作子报表
(2)制作子报表的注意事项
(3)制作父报表
(4)设置子报表数据源
1.3、使用Java填充模板文件
(1)引入依赖
(2)创建JasperReportsUtil工具类
(3)创建实体类
(4)放置模板文件
(5)创建测试控制器类
(6)运行测试
一、JasperReports实现父子报表
1.1、运行效果
这里是使用Jasper Studio制作的父子报表模板,以及通过Java填充数据之后生成的PDF文件,如下所示:
1.2、制作模板
制作父子报表,很明显需要有一个父报表、一个子报表,子报表嵌入到父报表里面,之后通过数据源进行渲染模板文件,首先创建子报表模板文件。
(1)制作子报表
在Jasper Studio中新建一个模板文件,这里我就叫做【sub_report.jrxml】,将模板文件中除了Detail区域之外,其余的都删除,如下图示:
接着添加三个Text Field文本域组件到Detail区域里面,调整样式,修改Detail区域高度等于30px,这里修改高度是为了让子报表的高度根据数据的来自动扩展。
到这里,子报表就算已经制作完成了,当然你要增加样式,你可以根据实际需求改动,我这里只是告诉你如何制作父子报表。
(2)制作子报表的注意事项
制作子报表的时候,需要注意以下几点:
- 子报表的高度,一般情况下,都设置成可伸展的,默认30px够了,高度根据实际需求调整。
- 一般情况下,子报表中只需要保留一个Detail区域即可。
- 子报表中,一般不需要设置外边距,可以设置页面的边距等于0。
(3)制作父报表
在Jasper Studio中新建一个报表文件,名称我这里叫做【main_report.jrxml】,然后添加Statid Text静态文本域,绘制表格,如下所示:
这里表格每一个字段的列宽是固定的,根据实际需求来调整,例如:我这里设置的name、price、amount三个字段的列宽度都是185px,和子报表中的列宽度是一致的,这是因为一会这三个列需要嵌入子报表组件,宽度设置成一样更好些。
- 接着,创建Field数据域字段,分别是:product、category、subData,其中subData的数据类型设置成【java.util.List】,这个字段就是子报表的数据源,是一个集合类型。
- 接着添加Text Field文本域字段,设置表格的数据行内容,如下所示:
- 在右上角的组件区域,将【SubReport】组件拖拽到Detail区域。
- 此时会弹出一个窗口,让你选择子报表文件的路径。
- 接着点击【Workspace resource】,从当前工作空间中选择子报表文件。
- 选择成功之后,调整一下子报表的样式,最终样式如下所示。
(4)设置子报表数据源
父报表制作完成之后,就可以设置父报表中子报表组件使用的数据源以及子报表的路径位置啦,首先选中子报表,在右下角区域可以设置表达式。一般情况下,子报表的路径都会设置成一个参数,然后在Java程序中动态的指定子报表的路径位置。
点击会弹出窗口,让你选择表达式字段,如下所示:
需要注意的是,数据源类型必须使用【new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{subData})】方式包装起来。
如果子报表路径不正确,则会报错:
net.sf.jasperreports.engine.JRException: Resource not found at: sub_report.jrxml.
如果子报表的数据源设置不正确,则会报错:
Unsupported subreport section type java.util.ArrayList.
到这里,我们的父子报表模板文件就制作成功啦,编译模板文件,生成对应的【】文件,将其放入到Java工程里面。
1.3、使用Java填充模板文件
(1)引入依赖
这里搭建的是SpringBoot工程,首先引入jasperreports的依赖,如下:
<?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.gitcode.demo</groupId>
<artifactId>jasper-demo</artifactId>
<version>1.0.0</version>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.5.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JasperReports 报表开发所需依赖 START -->
<!-- https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports -->
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.20.0</version>
<exclusions>
<!--
排除自带的itext依赖,因为自带的itext版本是 2.1.7.js10
这个版本在中央仓库里面没有,无法下载
-->
<exclusion>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入itext依赖,因为JasperReports中使用了itext操作PDF -->
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>
<!-- JasperReports 报表开发所需依赖 END -->
<!--
引入 poi 依赖,因为 jasper 底层操作excel使用的是poi组件
-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/webapp</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
</project>
(2)创建JasperReportsUtil工具类
package com.gitcode.demo.util;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.export.JRXlsExporter;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.export.*;
import org.springframework.core.io.ClassPathResource;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;
/**
* @version 1.0.0
* @Date: 2023/8/7 14:14
* @Author ZhuYouBin
* @Description: JasperReports 工具类
*/
public class JasperReportsUtil {
/**
* 使用 JasperReports 生成报表文件
* @param templatePath 模板文件路径及名称
* @param fileName 生成的文件名称
* @param fileType 生成的文件类型,例如: pdf、html、xls 等
* @param parameters 传递到 jrxml 模板文件中的数据参数
* @return 返回生成的报表文件路径
*/
public static String generateReport(String templatePath, String fileName, String fileType, Map<String, Object> parameters) throws Exception {
return generateReport(templatePath, fileName, fileType, parameters, new JREmptyDataSource());
}
public static String generateReport(String templatePath, String fileName, String fileType, Map<String, Object> parameters, JRDataSource dataSource) throws Exception {
// 1、获取 jasper 模板文件【采用流的方式读取】
ClassPathResource resource = new ClassPathResource(templatePath);
InputStream in = resource.getInputStream();
JasperReport jasperReport = (JasperReport) JRLoader.loadObject(in);
// 2、将 parameters 数据参数填充到模板文件中
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);
// 3、按照指定的 fileType 文件类型导出报表文件
if (fileType == null || "".equals(fileType.trim())) {
fileType = "pdf";
}
if (Objects.equals("pdf", fileType)) {
JasperExportManager.exportReportToPdfFile(jasperPrint, fileName + ".pdf");
} else if (Objects.equals("xls", fileType)) { // 导出 xls 表格
JRXlsExporter exporter = new JRXlsExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); // 设置导出的输入源
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(fileName + ".xls")); // 设置导出的输出源
// 配置信息
SimpleXlsReportConfiguration configuration = new SimpleXlsReportConfiguration();
configuration.setOnePagePerSheet(true); // 每一页一个sheet表格
exporter.setConfiguration(configuration); // 设置配置对象
exporter.exportReport(); // 执行导出
} else if (Objects.equals("xlsx", fileType)) { // 导出 xlsx 表格
JRXlsxExporter exporter = new JRXlsxExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); // 设置导出的输入源
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(fileName + ".xlsx")); // 设置导出的输出源
SimpleXlsxReportConfiguration configuration = new SimpleXlsxReportConfiguration();
configuration.setOnePagePerSheet(true); // 每一页一个sheet表格
exporter.setConfiguration(configuration);
exporter.exportReport(); // 执行导出
} else if (Objects.equals("html", fileType)) {
JasperExportManager.exportReportToHtmlFile(jasperPrint, fileName + ".html");
}
return null;
}
}
(3)创建实体类
- 创建主报表的实体类。
package com.gitcode.demo.domain;
import java.io.Serializable;
import java.util.List;
/**
* @version 1.0.0
* @Date: 2023/8/19 14:57
* @Author ZhuYouBin
* @Description: 主报表实体类
*/
public class MainEntity implements Serializable {
private String product;
private String category;
private List<SubEntity> subData;
// setter and getter
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public List<SubEntity> getSubData() {
return subData;
}
public void setSubData(List<SubEntity> subData) {
this.subData = subData;
}
}
- 创建子报表的实体类。
package com.gitcode.demo.domain;
import java.io.Serializable;
/**
* @version 1.0.0
* @Date: 2023/8/19 14:58
* @Author ZhuYouBin
* @Description: 子报表实体类
*/
public class SubEntity implements Serializable {
private String name;
private String price;
private Integer amount;
// setter and getter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public Integer getAmount() {
return amount;
}
public void setAmount(Integer amount) {
this.amount = amount;
}
}
(4)放置模板文件
将刚刚编译生成的父子报表文件放入到【src/main/resource】目录下面,如下所示:
(5)创建测试控制器类
package com.gitcode.demo.web;
import com.gitcode.demo.domain.MainEntity;
import com.gitcode.demo.domain.SubEntity;
import com.gitcode.demo.util.JasperReportsUtil;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @version 1.0.0
* @Date: 2023/8/12 16:34
* @Author ZhuYouBin
* @Description: 【父子报表】测试
*/
@RestController
@RequestMapping("/api/report")
public class ParentChildReportController {
@GetMapping("/sub-report")
public String exportFile(String format) throws Exception {
ClassPathResource resource = new ClassPathResource("main_report.jasper");
String templatePath = resource.getPath();
String fileName = "父子报表模板文件";
// 表格数据集是 JRBeanCollectionDataSource 类型的,也就是 JavaBean 实体类类型
List<MainEntity> data = this.getData(3);
JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(data);
// 设置 parameters 参数
Map<String, Object> parameters = new HashMap<>();
String subReportPath = new ClassPathResource("sub_report.jasper").getPath();
parameters.put("subReportPath", subReportPath); // 设置子报表的路径
// 执行导出操作
return JasperReportsUtil.generateReport(templatePath, fileName, format, parameters, dataSource);
}
private List<MainEntity> getData(int num) {
List<MainEntity> ansMap = new ArrayList<>();
for (int i = 0; i < num; i++) {
MainEntity main = new MainEntity();
main.setProduct("product_00" + i);
main.setCategory("phone_00" + i);
List<SubEntity> subData = new ArrayList<>();
int idx = (int)(Math.random() * 10) + i;
for (int j = 0; j < idx; j++) {
SubEntity subEntity = new SubEntity();
subEntity.setName("name_" + j);
subEntity.setPrice((int)(Math.random() * 100) + "");
subEntity.setAmount((int)(Math.random() * 100));
subData.add(subEntity);
}
main.setSubData(subData);
ansMap.add(main);
}
return ansMap;
}
}
(6)运行测试
启动工程,打开浏览器访问http://localhost:8080/api/report/sub-report?format=pdf地址,此时就会生成对应的PDF文件,打开PDF文件如下图所示:
到此,Jasper Stduio制作父子报表模板文件就完成啦,公众号回复【父子报表】获取源代码。
综上,这篇文章结束了,主要介绍如何使用Jasper Studio制作父子报表,并且通过Java + Parameters参数填充模板文件。