Search code examples
javajasper-reports

How to create nested table like jasper report?


I have been trying to create a jasper report which should look like the following image:

desired output

For my sample report, Out-1 and Out-2 are rows for the outer table and asd, asds, adasd are rows for the inner table.

I am trying to achieve it using nested tables. But the table takes the field and I can not assign field to the inner-table.

I am creating jasper reports in Java code rather than using SQL connection to the table for some reason.

JXML

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="report1" language="groovy" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="ec55a262-fec4-45f4-9b48-5be98088aafa">
    <property name="ireport.zoom" value="1.0"/>
    <property name="ireport.x" value="0"/>
    <property name="ireport.y" value="0"/>
    <subDataset name="New Dataset 1" uuid="cdef25ec-abc4-45ba-b70e-28d82716626b">
        <field name="sachNr" class="java.lang.String"/>
        <field name="akundenNr" class="java.lang.String"/>
        <field name="nestedTable" class="java.lang.String"/>
    </subDataset>
    <subDataset name="New Dataset 2" uuid="eec983aa-a227-4a28-9c44-73cbe31fa024">
        <field name="packStNr" class="java.lang.String"/>
        <field name="prodDatum" class="java.lang.String"/>
        <field name="stueck" class="java.lang.String"/>
    </subDataset>
    <parameter name=" artikeldatenTable" class="java.lang.String"/>
    <background>
        <band splitType="Stretch"/>
    </background>
    <title>
        <band height="79" splitType="Stretch"/>
    </title>
    <pageHeader>
        <band height="35" splitType="Stretch"/>
    </pageHeader>
    <columnHeader>
        <band height="61" splitType="Stretch"/>
    </columnHeader>
    <detail>
        <band height="125" splitType="Stretch">
            <componentElement>
                <reportElement key="table" x="0" y="28" width="360" height="50" uuid="fae14075-18a8-4fcc-b6f9-c14e3623e75d"/>
                <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">
                    <datasetRun subDataset="New Dataset 1" uuid="17a10558-3a43-47c2-809d-6362924e5015">
                        <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.JREmptyDataSource(1)]]></dataSourceExpression>
                    </datasetRun>
                    <jr:column width="137" uuid="7a8ca2ff-7a1a-41a0-8c30-b02f7be6e2b9">
                        <jr:tableHeader height="30" rowSpan="1">
                            <textField>
                                <reportElement x="0" y="0" width="137" height="30" uuid="0fe0e502-dea8-45f6-9f96-633f5db90cd8"/>
                                <textFieldExpression><![CDATA[$F{sachNr}]]></textFieldExpression>
                            </textField>
                        </jr:tableHeader>
                        <jr:detailCell height="42" rowSpan="1">
                            <componentElement>
                                <reportElement key="table" x="0" y="0" width="137" height="42" uuid="9e72ca8c-182d-41c0-a021-98493162c7da"/>
                                <jr:table>
                                    <datasetRun subDataset="New Dataset 2" uuid="7475e256-4bc5-4e88-be92-b6d2eb2fd4c5">
                                        <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.JREmptyDataSource()]]></dataSourceExpression>
                                    </datasetRun>
                                    <jr:column width="90" uuid="cd851138-c894-459f-91c9-636c3602b247">
                                        <jr:detailCell height="20" rowSpan="1">
                                            <textField>
                                                <reportElement x="0" y="0" width="90" height="20" uuid="ce21338e-c593-4bae-b7c4-08741127575c"/>
                                                <textFieldExpression><![CDATA[$F{packStNr}]]></textFieldExpression>
                                            </textField>
                                        </jr:detailCell>
                                    </jr:column>
                                    <jr:column width="90" uuid="5f2b25a9-e34a-4698-b1ab-e8843dafec3c">
                                        <jr:detailCell height="20" rowSpan="1">
                                            <textField>
                                                <reportElement x="0" y="0" width="90" height="20" uuid="5487305d-d22f-46fa-97bc-88e671f7085c"/>
                                                <textFieldExpression><![CDATA[$F{prodDatum}]]></textFieldExpression>
                                            </textField>
                                        </jr:detailCell>
                                    </jr:column>
                                    <jr:column width="90" uuid="93c28f55-9bda-4be0-989a-03ce573715ba">
                                        <jr:detailCell height="20" rowSpan="1">
                                            <textField>
                                                <reportElement x="0" y="0" width="90" height="20" uuid="53c4e2b3-7591-49b3-9b13-50cc1f16641a"/>
                                                <textFieldExpression><![CDATA[$F{stueck}]]></textFieldExpression>
                                            </textField>
                                        </jr:detailCell>
                                    </jr:column>
                                </jr:table>
                            </componentElement>
                        </jr:detailCell>
                    </jr:column>
                    <jr:column width="90" uuid="f898d2e7-1c2f-48fe-9357-61ee5d767a51">
                        <jr:tableHeader height="30" rowSpan="1">
                            <textField>
                                <reportElement x="0" y="0" width="90" height="30" uuid="16b70095-2900-46f0-96ec-d638edb784f3"/>
                                <textFieldExpression><![CDATA[$F{akundenNr}]]></textFieldExpression>
                            </textField>
                        </jr:tableHeader>
                        <jr:detailCell height="42" rowSpan="1"/>
                    </jr:column>
                    <jr:column width="111" uuid="0121d9c8-d1f0-447f-84ed-15385693a072">
                        <jr:tableHeader height="30" rowSpan="1"/>
                        <jr:detailCell height="42" rowSpan="1"/>
                    </jr:column>
                    <jr:column width="90" uuid="ca27e48c-a1d6-4c32-a1de-2580b6786b3d">
                        <jr:tableHeader height="30" rowSpan="1"/>
                        <jr:detailCell height="42" rowSpan="1"/>
                    </jr:column>
                </jr:table>
            </componentElement>
        </band>
    </detail>
    <columnFooter>
        <band height="45" splitType="Stretch"/>
    </columnFooter>
    <pageFooter>
        <band height="54" splitType="Stretch"/>
    </pageFooter>
    <summary>
        <band height="42" splitType="Stretch"/>
    </summary>
</jasperReport>

Outer Table POJO

import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;

public class OuterTable {

    private String sachNr;
    private String akundenNr;
    private String SUBREPORT_DIR;
    private JRBeanCollectionDataSource nestedTable;

    public String getSachNr() {
        return sachNr;
    }
    public void setSachNr(String sachNr) {
        this.sachNr = sachNr;
    }
    public String getAkundenNr() {
        return akundenNr;
    }
    public void setAkundenNr(String akundenNr) {
        this.akundenNr = akundenNr;
    }
    public String getSUBREPORT_DIR() {
        return SUBREPORT_DIR;
    }
    public void setSUBREPORT_DIR(String sUBREPORT_DIR) {
        SUBREPORT_DIR = sUBREPORT_DIR;
    }

    public JRBeanCollectionDataSource getNestedTable() {
        return nestedTable;
    }
    public void setNestedTable(JRBeanCollectionDataSource nestedTable) {
        this.nestedTable = nestedTable;
    }

}

Inner Table POJO

public class InnerTable {

    private String packStNr;
    private String prodDatum;
    private Integer stueck;

    public String getPackStNr() {
        return packStNr;
    }
    public void setPackStNr(String packStNr) {
        this.packStNr = packStNr;
    }
    public String getProdDatum() {
        return prodDatum;
    }
    public void setProdDatum(String prodDatum) {
        this.prodDatum = prodDatum;
    }
    public Integer getStueck() {
        return stueck;
    }
    public void setStueck(Integer stueck) {
        this.stueck = stueck;
    }

}

My Controller

private void generateLagerbestand(List<Artikeldaten> artikelList) throws JRException, FileNotFoundException {
    JasperReport jasperReport;

    Map<String, Object> parameters = new HashMap<String, Object>();

    List <OuterTable> outerTableList = new ArrayList<OuterTable>();
    for(int i = 0; i < artikelList.size(); i++) {


        List <InnerTable> innerTableList = new ArrayList<InnerTable>();
        List<Lager> lagersList = artikelList.get(i).getLagers();
        for(int j = 0; j < lagersList.size(); j++) {

            InnerTable innerTable = new InnerTable();

            innerTable.setPackStNr(lagersList.get(j).getPackstnr());
            innerTable.setProdDatum(lagersList.get(j).getProddatum().toString());
            innerTable.setStueck(lagersList.get(j).getStueck());

            innerTableList.add(innerTable);
        }

        OuterTable outerTable = new OuterTable();

        outerTable.setAkundenNr(artikelList.get(i).getAkundennr());
        outerTable.setSachNr(artikelList.get(i).getSachnr());

        JRBeanCollectionDataSource nestedTable = new JRBeanCollectionDataSource(innerTableList);
        outerTable.setNestedTable(nestedTable);

        outerTableList.add(OuterTable);
    }

    try {

        jasperReport = JasperCompileManager.compileReport("PathToMyReport\\MyReport.jrxml");
        JRBeanCollectionDataSource outerTableItems = new JRBeanCollectionDataSource(outerTableList);


        // artikeldatenTable is outerTable
        parameters.put("artikeldatenTable", outerTableItems);
        JREmptyDataSource jrEmptyDataSource = new JREmptyDataSource();
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, jrEmptyDataSource);
        OutputStream outputStream = new FileOutputStream(new File("PathToMyPDF:\\MyPDF.pdf"));

        JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
    } catch(Exception ex) {
        ex.printStackTrace();
    }
}

Solution

  • You are missing the concept of the detail band, the detail will iterate on the datasource you pass to report

    JasperFillManager.fillReport(jasperReport, parameters, jrEmptyDataSource)
    

    That's an empty datasource!, it will not iterate at all.

    Instead, pass

    JRBeanCollectionDataSource outerTableItems = new JRBeanCollectionDataSource(outerTableList);
    

    To the report, define the fields of OuterTable in your main report (not a subDatasource) including nestedTable

    <field name="nestedTable" class="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource"/>
    

    Then

    In detail band you put textFields related to first level "Out-1" and "Out-2" in your example.

    Bellow you add the jr:table component taking the datasource from the bean

    <datasetRun subDataset="New Dataset 1" uuid="17a10558-3a43-47c2-809d-6362924e5015">
          <dataSourceExpression><![CDATA[$F{nestedTable}]]></dataSourceExpression>
    </datasetRun>
    

    In general within the bean I would not have JRBeanCollectionDatasource but instead store the data in a normal List<>, the reason is that a JRDatasource is consumable you can only use it once, see this to understand better How to use same JRBeanCollectionDataSource on multiple sub reports?