如何为含多子报表的Jasper Report注入JRBeanCollectionDataSource数据?
嘿,作为Jasper新手,遇到子报表数据源的问题很正常,我来一步步帮你搞定!
首先,你当前的代码用了JREmptyDataSource填充主报表,但没有给子报表传递任何数据集合,而且主报表里的子报表配置用了REPORT_CONNECTION(JDBC连接),这和你要使用的JRBeanCollectionDataSource(Bean集合数据源)不匹配,这就是问题所在。
下面是具体的解决步骤:
1. 调整JasperSoft Studio中的主报表配置
首先需要修改主报表里的子报表设置,去掉JDBC连接配置,换成Bean数据源的参数传递:
步骤1.1 为主报表添加参数
在JasperSoft Studio中打开主报表,添加两个参数:
- 参数名:
claimHistoryList,类型选择java.util.List - 参数名:
claimDetailList,类型选择java.util.List
步骤1.2 修改子报表的数据源配置
把每个子报表的<connectionExpression>替换成<dataSourceExpression>,用我们刚添加的参数来创建JRBeanCollectionDataSource:
第一个子报表(ClaimHistory)的配置修改后:
<subreport> <reportElement positionType="Float" x="18" y="1" width="522" height="65" uuid="f3063ed3-775f-47df-8306-a939aecc9d89"/> <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($P{claimHistoryList})]]></dataSourceExpression> <subreportExpression><![CDATA["ClaimHistory.jasper"]]></subreportExpression> </subreport>
第二个子报表(ClaimDetail)的配置修改后:
<subreport> <reportElement positionType="Float" x="18" y="2" width="522" height="65" uuid="a70d75e0-b9d0-4f52-bb84-090993c2e90b"/> <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($P{claimDetailList})]]></dataSourceExpression> <subreportExpression><![CDATA["ClaimDetail.jasper"]]></subreportExpression> </subreport>
注意:如果你的子报表需要其他参数,也可以在主报表里定义并通过子报表的
<parameterMapExpression>传递,不过这里我们先解决数据源的核心问题。
2. 修改Java代码,传递子报表数据集合
接下来要在Java代码里准备好子报表需要的数据,然后通过参数传递给主报表:
步骤2.1 准备业务数据
先创建你的实体类(比如ClaimHistory和ClaimDetail),并准备好对应的List集合:
// 你的实体类,必须提供getter方法,字段名要和子报表里的字段对应 class ClaimHistory { private String claimId; private String claimDate; private String claimType; // 构造方法 public ClaimHistory(String claimId, String claimDate, String claimType) { this.claimId = claimId; this.claimDate = claimDate; this.claimType = claimType; } // Getter方法,Jasper会通过这些方法获取字段值 public String getClaimId() { return claimId; } public String getClaimDate() { return claimDate; } public String getClaimType() { return claimType; } } class ClaimDetail { private String detailId; private String itemName; private Double amount; public ClaimDetail(String detailId, String itemName, Double amount) { this.detailId = detailId; this.itemName = itemName; this.amount = amount; } public String getDetailId() { return detailId; } public String getItemName() { return itemName; } public Double getAmount() { return amount; } } // 准备数据集合 List<ClaimHistory> claimHistoryList = new ArrayList<>(); claimHistoryList.add(new ClaimHistory("CH001", "2024-01-01", "医疗报销")); claimHistoryList.add(new ClaimHistory("CH002", "2024-02-15", "意外报销")); List<ClaimDetail> claimDetailList = new ArrayList<>(); claimDetailList.add(new ClaimDetail("CD001", "药品费", 150.00)); claimDetailList.add(new ClaimDetail("CD002", "检查费", 300.00));
步骤2.2 把数据集合放入参数Map
将上面的List集合添加到参数Map中,同时设置报表文件目录,确保程序能找到子报表文件:
Map<String, Object> params = new HashMap<>(); params.put("claimHistoryList", claimHistoryList); params.put("claimDetailList", claimDetailList); // 设置报表文件所在目录,这样子报表可以直接用文件名查找 params.put(JRParameter.REPORT_FILE_DIR, "E:/jasper/");
步骤2.3 填充并导出报表
修改填充报表的代码,把参数Map传进去(主报表没有自己的数据,所以继续用JREmptyDataSource即可):
String jasperFile = "E:/jasper/jasperFile.jrxml"; FileInputStream inputStream = new FileInputStream(jasperFile); JasperReport jasperReport = JasperCompileManager.compileReport(inputStream); // 填充报表,传入参数和空数据源 JasperPrint print = JasperFillManager.fillReport(jasperReport, params, new JREmptyDataSource()); List<JasperPrint> jasperPrintList = new ArrayList<>(); jasperPrintList.add(print); JRPdfExporter exporter = new JRPdfExporter(); exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList)); exporter.setExporterOutput(new SimpleOutputStreamExporterOutput("E:/output.pdf")); exporter.exportReport();
关键注意事项
- 子报表字段与实体类匹配:子报表里的字段名称必须和实体类的getter方法对应,比如子报表里的
claimId字段对应实体类的getClaimId()方法,大小写敏感! - 子报表路径问题:确保
ClaimHistory.jasper和ClaimDetail.jasper文件在你设置的REPORT_FILE_DIR目录下,或者在子报表表达式里使用绝对路径。 - 编译子报表:最好在JasperSoft Studio里提前编译好子报表生成
.jasper文件,这样Java程序运行时不需要再编译,提升效率。
这样修改后,子报表就能正确获取到JRBeanCollectionDataSource里的数据了,导出的PDF应该就能显示子报表的内容啦!
内容的提问来源于stack exchange,提问作者Ari Wibowo

