Iman Iman - 16 days ago 7
Java Question

Add custom data source to Jaspersoft Studio

I am trying to fill a table by passing a custom data source to it.
I have created a simple report with a table on it. The report it self gets the data from a ms sql database. I have written a java class similar to the class in this Example. But I get no value in table. At the example there is no scriptlet. I have checked the

(String) this.getFieldValue("KN_FormelGG");
line of code. It gets the data from field and can show it on report. So I guess the bean data source is not filled. I call the fill Table method in a
afterGroupInit
. How can I use Collection of data from java in jasper? I tried also adding the java bean in dataset and query dialog, but it did not help me either. Should I add the scriptlet to subreport/table? The main emphasis of my problem having the custom data source in a scriptlet. I went the other problem through, but I still get no answer. I added
$P{FieldDataSource}.getData()
to check the data, but it delivers null.

java class 1:

package testProjektIman.scriptlets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.jasperreports.engine.JRDefaultScriptlet;
import net.sf.jasperreports.engine.JRScriptletException;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;

public class FillTable extends JRDefaultScriptlet {

@Override
public void afterGroupInit(final String id)
throws JRScriptletException {
fillTable();
}
public ArrayList<String> splitGGArray(final String kNFormelGG) {
ArrayList<String> fieldNames = new ArrayList<>();
String[] array = (kNFormelGG.split(" "));
for (String sub : array) {
fieldNames.add(sub);
}
return fieldNames;
}

public Map<String, Object> fillTable()
throws JRScriptletException {
String kNFormelGG = null;
kNFormelGG = (String) this.getFieldValue("KN_FormelGG");
List<TableCells> listTableCells = new ArrayList<>();
TableCells tableCell;
for (String fn : splitGGArray(kNFormelGG)) {
tableCell = new TableCells();
tableCell.setFieldName(fn);
listTableCells.add(tableCell);
}
JRBeanCollectionDataSource tableCellJRBean = new JRBeanCollectionDataSource(listTableCells);
Map<String, Object> parameters = new HashMap<>();
parameters.put("FieldDataSource", tableCellJRBean);
return parameters;
}

}


Java class 2

package testProjektIman.scriptlets;
public class TableCells {
private String fieldName;
private String keyFormel;
private String mK;
private String notation;
private String Item;
//getters setters
}


jrxml

<?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="tableAutoFill" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="37fc3a9c-38e9-41be-9039-56249c5283d7">
<subDataset name="TableDataSource" uuid="5999e646-aeec-4b2b-b29b-68e897d56999">
<queryString>
<![CDATA[]]>
</queryString>
<field name="fieldName" class="java.lang.String"/>
</subDataset>
<scriptlet name="Filltable" class="testProjektIman.scriptlets.FillTable"/>
<parameter name="FieldDataSource" class="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource" isForPrompting="false"/>
<queryString>
<![CDATA[select * from "KennzahlReferenz2015_QIBericht", "Images"
where LB_ID = 62
and KN_OffiziellGruppe = 3
and IMG_ID = 1]]>
</queryString>
<field name="KN_Id" class="java.lang.Integer"/>
<field name="KN_FormelZ" class="java.lang.String"/>
<field name="KN_FormelGG" class="java.lang.String"/>
<group name="id">
<groupExpression><![CDATA[$F{KN_Id}]]></groupExpression>
<groupHeader>
<band height="50"/>
</groupHeader>
<groupFooter>
<band height="50"/>
</groupFooter>
</group>
<detail>
<band height="191" splitType="Stretch">
<componentElement>
<reportElement x="50" y="18" width="260" height="120" uuid="942ab836-df83-4a2f-8215-845073ad163f">
<property name="com.jaspersoft.studio.layout" value="com.jaspersoft.studio.editor.layout.VerticalRowLayout"/>
<property name="com.jaspersoft.studio.table.style.table_header" value="Table_TH"/>
<property name="com.jaspersoft.studio.table.style.column_header" value="Table_CH"/>
<property name="com.jaspersoft.studio.table.style.detail" value="Table_TD"/>
</reportElement>
<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="TableDataSource" uuid="b554ada5-c388-4534-af8e-93571a417adb">
<parametersMapExpression><![CDATA[$P{FieldDataSource}]]></parametersMapExpression>
<dataSourceExpression><![CDATA[$P{FieldDataSource}]]></dataSourceExpression>
</datasetRun>
<jr:column width="100" uuid="f57e4e8e-8a2e-405f-b9ab-7a704e0986fd">
<property name="com.jaspersoft.studio.components.table.model.column.name" value="Column1"/>
<jr:columnHeader style="Table_CH" height="30">
<staticText>
<reportElement x="0" y="0" width="100" height="30" uuid="9a55dd73-71e3-4559-9a8b-17d98bf17753"/>
<textElement>
<font isBold="true"/>
</textElement>
<text><![CDATA[FieldName]]></text>
</staticText>
</jr:columnHeader>
<jr:detailCell style="Table_TD" height="30">
<textField>
<reportElement x="0" y="0" width="100" height="30" uuid="c3e6ccfc-9f91-4d7a-800a-613f5dded928"/>
<textFieldExpression><![CDATA[$F{fieldName}]]></textFieldExpression>
</textField>
</jr:detailCell>
</jr:column>
</jr:table>
</componentElement>
</band>
</detail>
</jasperReport>

Answer

Data Adapter

Create a data adapter file, for example adapter.xml, through the user interface. The contents might resemble:

<?xml version="1.0" encoding="UTF-8" ?>
<beanDataAdapter class="net.sf.jasperreports.data.bean.BeanDataAdapterImpl">
    <name>YourClass</name>
    <factoryClass>com.yourcompany.jasper.JRDataSourceFactory</factoryClass>
    <methodName>createCollection</methodName>
    <useFieldDescription>false</useFieldDescription>
</beanDataAdapter>

Data Class

Create a class that has a createCollection method, as per the data adapter definition above:

package com.compay.jasper;

public class JRDataSourceFactory {
    /**
     * @return A collection of data for the report.
     */
    public static Collection<YourClass> createCollection() {
        return Arrays.asList( new YourClass() );
    }
}

Set Report Properties

Ensure that the report has the following property (links the report to the custom data adapter):

<property name="com.jaspersoft.studio.data.defaultdataadapter" value="adapter.xml"/>

Set Report Fields

The report fields should now be able to reference the bean properties:

<field name="yourObject.property" class="java.lang.String">
    <fieldDescription><![CDATA[yourObject.property]]></fieldDescription>
</field>

It's important that the fieldDescription element contain a value that reflects the bean property (i.e., the Java code you would normally call to retrieve the value from the bean on an instance of that bean).

Application

The JRDataSourceFactory class stands alone -- it's used by the data adapter to create a collection of bean instances. The static method (createCollection) does the work here and does not, indeed cannot, use inheritance.

Example

If possible, borrow the field names for the report from the bean's attributes. The code as written in the question makes it difficult to discern where the value for KN_Id comes from.

The following example links the data adapter to the field names in the report.

Bean Class

A bean exposes some properties:

package com.company.domain;

public final class Student extends Entity {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return this.firstName;
    }

    public String getLastName() {
        return this.lastName;
    }
}

Data Adapter

The data adapter looks like:

<?xml version="1.0" encoding="UTF-8" ?>
<beanDataAdapter class="net.sf.jasperreports.data.bean.BeanDataAdapterImpl">
    <name>Student</name>
    <factoryClass>com.company.jasper.JRDataSourceFactory</factoryClass>
    <methodName>createCollection</methodName>
    <useFieldDescription>false</useFieldDescription>
</beanDataAdapter>

Data Class

The data class creates instances of the bean:

package com.compay.jasper;

import com.company.domain.Student;

public class JRDataSourceFactory {
    public static Collection<Student> createCollection() {
        return Arrays.asList( new Student() );
    }
}

Report Fields

The report fields reflect the bean fields:

<field name="firstName" class="java.lang.String">
    <fieldDescription><![CDATA[firstName]]></fieldDescription>
</field>
<field name="lastName" class="java.lang.String">
    <fieldDescription><![CDATA[lastName]]></fieldDescription>
</field>

A collection of "student" instances is passed into the report. In this example, the collection contains a single instance. In your example, the collection could contain many instances. As the report library iterates over the collection, different values of firstName and lastName will be made available.

The mechanics for how the Student instance data is initially populated is outside the scope of this answer. As far as the reporting tool is concerned, it simply uses pre-populated instances of Student. If FillTable populates TableCells, that's not a concern for the reporting tool.