简体   繁体   中英

How to pass main report data source to subreport (JasperReports)?

I'm using JasperReports and I fill the JRDataSource for ther report. Now, I want to pass the main REPORT_DATA_SOURCE to the subreport. How can I do this?

As far as I know the REPORT_DATA_SOURCE is a consumable object, so it can only be used once, right?. Can I copy this data source and pass it?

BTW: I use iReport for creating the layout.

You can pass datasource via the built-in REPORT_DATA_SOURCE parameter.

The example:

<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>

You can create new instance of datasource based on variable, parameter or field.

The sample:

<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>

Another sample:

<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>

Or you can pass the datasource via the parameter:

<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>

Note: Using the same (with master report) datasource in subreport can cause the effect of loosing the first row in the subreport . You can read Why is the first record missing from my subreport? post for understanding how to avoid this issue.

we suppose that the datasource parameter is "dataSourceParam" and the datasource value (list) is "dataSourceList" in java class we put :

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

in main report template we put in parameters declaration :

<parameter name="dataSourceParam" class="net.sf.jasperreports.engine.JRDataSource"/>

then in subreport tag we put :

<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>

"REPORT_DATA_SOURCE" is a consumable object, You can used as much time as you want.

I have test the datasource as xml File dataSource, and won't be appeared as ALEX said.

"this will not be loosing the first row in subreport."

I think may be i use the xpath to select, so every time won't be loss records.

If you use JDBC Database as the datasource, pealse pass the sql as parameter to subreport.

If you use ResultSet as the parameter, maybe loss one record as you definition the subreport in detail bands.

Yes, you need to be careful about how to pass a data source. With a SQL connection, you can just pass a Connection Expression like $P{REPORT_CONNECTION} . Then the subreport has its own SQL query.

In your case you want to pass the actual data. Depending on the details, it might be as simple as just defining a Parameter Map Expression like $P{REPORT_PARAMETERS_MAP} . It's on a different tab in that same window where you set the subreport connection in iReport. Often this is sufficient to pass the data source.

But you might need a little code to handle things. Consider this example with a CSV data source re-used in subreports. The reason why you can't just use the JRParameter.REPORT_DATA_SOURCE object is because the index row pointer is never reset, so passing that original object into the subreport will bring the recordset to its close prematurely. We solved this with a minimal helper class:

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; 
    } 
}

It's an old question already answered but i get to pass the undelying bean to the subreport, avoiding the loss of the first record or passing all the records to subreport. This solution has the advantage that the subreport can be used as main report and is "simply" pass the actual record as subreport datasource (using groovy as report lang):

<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>

I had a situation where I had a table in a subreport. The subreport only has a title band and a summary band, with the table in the summary. I wanted to use the subreport datasource for the table as well, but could not get either of the approaches in the accepted answer to work. So here as an alternative approach that is working great in version 6.6.0:

In the main report:

        <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>

The key here is that the List is being passed as a parameter and NOT as a datasource like:

<dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{drawingRevision}.getPartsList())]]></dataSourceExpression>

The subreport then includes:

...
<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>
...

Note that in the subreport you also need to include the property whenNoDataType="AllSectionsNoDetail" or something like that, otherwise the subreport will be blank because it has no data.

只是为了完成 Alex K 的回答,真正让它对我有用的是按如下方式克隆数据源

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM