如何将主报表数据源传递给子报表 (JasperReports)?

43
我正在使用JasperReports,并为报告填充JRDataSource。现在,我想将主REPORT_DATA_SOURCE传递给子报表。我该怎么做?
据我所知,REPORT_DATA_SOURCE是一个可消耗的对象,因此它只能使用一次,对吗?我能复制这个数据源并传递它吗?
顺便说一下:我使用iReport创建布局。
7个回答

48
你可以通过内置参数REPORT_DATA_SOURCE传递数据源
示例:
<subreport>
    <reportElement x="261" y="25" width="200" height="100"/>
    <dataSourceExpression><![CDATA[$P{REPORT_DATA_SOURCE}]]></dataSourceExpression>
    <subreportExpression><![CDATA[$P{SUBREPORT_DIR} + "subreport.jasper"]]></subreportExpression>
</subreport>

您可以基于变量、参数或字段创建数据源的新实例。

示例:

<variable name="HeadingsCollection" class="java.util.Collection" calculation="System">
    <initialValueExpression><![CDATA[new java.util.ArrayList()]]></initialValueExpression>
</variable>
...
<subreport>
    <reportElement x="0" y="0" width="515" height="20"/>
    <subreportParameter name="ReportTitle">
        <subreportParameterExpression><![CDATA[$P{ReportTitle}]]></subreportParameterExpression>
    </subreportParameter>
    <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($V{HeadingsCollection})]]></dataSourceExpression>
    <subreportExpression class="java.lang.String"><![CDATA["HeadingsReport.jasper"]]></subreportExpression>
</subreport>

另一个示例:

<field name="cast" class="java.util.Collection"/>
...
<subreport>
    <reportElement positionType="Float" x="15" y="25" width="245" height="20" isRemoveLineWhenBlank="true" backcolor="#99CCFF"/>
    <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{cast})]]></dataSourceExpression>
    <subreportExpression class="java.lang.String"><![CDATA["JRMDbCastSubreport.jasper"]]></subreportExpression>
</subreport>

或者你可以通过参数传递数据源

<parameter name="SubreportDataSource" class="net.sf.jasperreports.engine.JRDataSource"/>
...
<subreport>
    <reportElement positionType="Float" x="15" y="25" width="245" height="20" isRemoveLineWhenBlank="true"/>
    <dataSourceExpression>$P{SubreportDataSource}</dataSourceExpression>
    <subreportExpression class="java.lang.String"><![CDATA["Subreport.jasper"]]></subreportExpression>
</subreport>

注意: 在子报表中使用相同的(主报告)数据源可能会导致丢失子报表中的第一行。您可以阅读为什么我的子报表中缺少第一条记录?帖子以了解如何避免此问题。


“Why is the…”的链接无法访问。需要登录和密码。 - zellus
2
《为什么我的子报表缺少第一条记录》(http://community.jaspersoft.com/wiki/why-first-record-missing-my-subreport)现在可以在无需登录的情况下再次查看。 - Scott Carlson
我已经发布了一个解决方案,可以防止“记录丢失”问题,并允许使用子报表作为主报表(使用子报表数据源和字段)。 - jneira
请参考以下链接:https://dev59.com/m4rda4cB1Zd3GeqPKUgV - Laurent B
如何在子报表中实际使用“cast”参数? - yglodt

4

假设数据源参数为"dataSourceParam",数据源值(列表)为"dataSourceList"。

在Java类中我们写:

final Map<String, Object> params = new HashMap<String, Object>();
JRDataSource dataSource = new ListOfArrayDataSource( dataSourceList, 
                          new String[] {"date", "age", "adress", "email"});
params.put("dataSourceParam",dataSourceList);**

在主报告模板中,我们放置了参数声明:
<parameter name="dataSourceParam" class="net.sf.jasperreports.engine.JRDataSource"/>

然后在子报告标签中添加:

<subreport isUsingCache="true">
    <reportElement key="subreport-1" stretchType="RelativeToTallestObject" isPrintRepeatedValues="false" x="112" y="45" width="338" height="29"/>
    <subreportParameter name="otherParameter">
        <subreportParameterExpression><![CDATA[$P{sumM1}]]></subreportParameterExpression>
    </subreportParameter>
    <dataSourceExpression><![CDATA[$P{dataSourceParam}]]></dataSourceExpression>
    <subreportExpression class="net.sf.jasperreports.engine.JasperReport"><![CDATA[$P{subReportFile}]]></subreportExpression>
</subreport>

2
是的,您需要注意如何传递数据源。对于SQL连接,您可以只传递一个连接表达式,例如$P{REPORT_CONNECTION}。然后子报表就有了自己的SQL查询。
在您的情况下,您想要传递实际数据。根据详细情况,可能只需定义一个参数映射表达式,例如$P{REPORT_PARAMETERS_MAP}。它位于iReport中设置子报表连接的同一窗口的不同选项卡中。通常这就足以传递数据源。
但是您可能需要一些代码来处理事情。考虑以下使用CSV数据源重复使用子报表的示例。不能仅使用JRParameter.REPORT_DATA_SOURCE对象,因为索引行指针从未重置,因此将原始对象传递到子报表将会过早地关闭记录集。我们使用了一个最小的帮助类来解决这个问题:
package com.jaspersoft.untested_unsupported; 

import java.io.File; 
import java.io.FileNotFoundException; 
import net.sf.jasperreports.engine.JRDataSource; 
import net.sf.jasperreports.engine.data.JRCsvDataSource; 

public class CsvDataSourceFactory { 
    public static JRDataSource getDataSource(String fileName, boolean firstRowHeaders) throws FileNotFoundException { 
        JRCsvDataSource csvDs = new JRCsvDataSource(new File(fileName)); 
        csvDs.setUseFirstRowAsHeader(firstRowHeaders); 
        return csvDs; 
    } 
}

1

“REPORT_DATA_SOURCE”是一个可消耗的对象,您可以随时使用。

我已经测试了将数据源作为xml文件dataSource,不会像ALEX说的那样出现问题。

“这不会在子报表中丢失第一行。”

我认为可能是我使用XPath进行选择,因此每次都不会丢失记录。

如果您使用JDBC数据库作为数据源,请将SQL作为参数传递到子报表中。

如果您将ResultSet作为参数使用,则可能会由于在详细信息面板中定义了子报表而导致丢失一条记录。


1
我曾经遇到这样一个情况,在子报表中有一张表。该子报表仅有标题和摘要两个区域,而表格位于摘要区域中。我想同时将子报表的数据源用于该表格中,但是无法使得已接受答案中的任何一种方法奏效。因此,在6.6.0版本中,我使用了以下替代方法,它效果很好:
在主报表中:
        <subreport>
            <reportElement x="0" y="0" width="468" height="0" uuid="c057b890-3889-43dd-8634-bbf2e857cc0d"/>
            <subreportParameter name="partsList">
                <subreportParameterExpression><![CDATA[$F{drawingRevision}.getPartsList()]]></subreportParameterExpression>
            </subreportParameter>
            <subreportExpression><![CDATA["static/engineering/drawings/subreports/DrawingPartsList.jasper"]]></subreportExpression>
        </subreport>

关键在于将 List 作为参数传递,而不是像数据源一样:
<dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{drawingRevision}.getPartsList())]]></dataSourceExpression>

子报表包括:

...
<parameter name="partsList" class="java.util.List" isForPrompting="false"/>
...
<summary>
    <band height="60" splitType="Stretch">
        <componentElement>
            <reportElement key="table" style="table" x="0" y="0" width="468" height="60" uuid="09499b35-b122-4fe4-a2b3-d91d6a19b2ab"/>
            <jr:table xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd" whenNoDataType="AllSectionsNoDetail">
                <datasetRun subDataset="PartList" uuid="87fcbcc9-f0f0-4397-87f2-237201fc1857">
                    <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($P{partsList})]]></dataSourceExpression>
                </datasetRun>
...

请注意,在子报表中,您还需要包括属性whenNoDataType="AllSectionsNoDetail"或类似的内容,否则子报表将为空,因为它没有数据。

1

这个问题早已经有人回答过了,但是我可以将底层的 bean 传递给子报表,避免丢失第一条记录或者将所有记录传递给子报表。 这个解决方案的优势在于子报表可以作为主报表使用,并且只需要将实际记录作为子报表的数据源传递(使用 Groovy 作为报表语言):

<subreport>
    <reportElement x="261" y="25" width="200" height="100"/>
    <dataSourceExpression><![CDATA[new JRBeanCollectionDataSource(
       $P{REPORT_DATA_SOURCE}.data.toList().subList($V{REPORT_COUNT}-1,$V{REPORT_COUNT})]]></dataSourceExpression>
    <subreportExpression><![CDATA[$P{SUBREPORT_DIR} + "subreport.jasper"]]></subreportExpression>
</subreport>

在我旧版本的Java中,至少.data不是一个可见字段。 - Noumenon
1
是的,Groovy允许您访问私有字段,不确定是否有一种方法可以使用Java读取它。 - jneira

0

为了完善Alex K的答案,对我真正起作用的是按照以下方式克隆数据源

<dataSourceExpression><![CDATA[((net.sf.jasperreports.engine.data.JRBeanCollectionDataSource) $P{REPORT_DATA_SOURCE}).cloneDataSource()]]></dataSourceExpression>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接