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.
Solution 1:
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.
Solution 2:
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;
}
}
Solution 3:
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>