考虑以下data set:
Name Mode Tally ------ ------ ------- N_1 M_1 1000 N_2 M_3 4000 N_3 M_2 500 N_4 M_1 2000 N_5 M_3 8000
模式的总计为:
Mode Total ------ ------- M_1 3000 M_2 500 M_3 12000
数据按月分组,并按名称排序;它无法按模式分组。 模式的整个值集未知但有限(例如M_1
,M_2
,M_3
,M_x
,M_y
,M_z
,等等。)
模式总计必须在摘要频段中显示,该频段看起来像是使用JRDistinctCountIncrementer递增的变量的良好候选者(使用增量工厂类名JRDistinctCountIncrementerFactory)。部分问题是documentation缺乏。
清楚说明预期用途:
请注意 Tx小计部分中的元素如何重用现有样式并与现有列对齐。只要Scriptlet可以公开可迭代数据,就可以使用Scriptlet将字符串值写入摘要频段。
必须在填写报告行之后检索每个不同元组的总计列表(例如,模式和计数)。然后该列表作为JRMapCollectionDataSource传递到子报表。该子报告位于主报告的摘要范围内。
为此,必须按以下方式创建变量:
modes
java.util.Map
?No Calculation Function
?new AbstractMap.SimpleEntry( $F{mode}, $F{tally} )
?None
Report
这将允许子报告的数据源表达式:
new JRMapCollectionDataSource( $V{modes} )
创建MappedIncrementerFactory
和MappedIncrementer
,类似于JRDistinctCountIncrementerFactory
和JRDistinctCountIncrementer
。
预先计算总计并使用数据对象模型传递它们。例如:
public class DataSetItem {
public String getName() { ... }
public String getMode() { ... }
public Integer getTally() { ... }
}
public class DataSet {
public List<DataSetItem> getDataSetItemList() { ... }
public Map<String, Integer> getDataSetTotals() { ... }
}
public class DataSetFactory {
/** Returns a single instance that has the list of items and totals. */
public List<DataSet> createDataSetItemCollection() { ... }
}
使用Scriptlet并为值返回JRDataSource
。
如何创建包含键/值对的类型集合变量(映射),其中每个值是与键名匹配的报表行的总和?
答案 0 :(得分:3)
scriplet将汇总并存储数据到稍后(在所有细节之后)返回数据源。注意:不考虑Nullpointer
或数据源的其他问题。
public class Scriplet extends JRDefaultScriptlet {
private Map<String, TxSubTotal> subTotals; //Use map for quick access
private int lastRecNr = 0; //Jasper has a bad habit of calling twice
public Scriplet(){
super();
subTotals = new HashMap<>();
}
@Override
public void afterDetailEval() throws JRScriptletException {
int recNr = ((Integer)this.getVariableValue("REPORT_COUNT"));
if (lastRecNr==recNr){ //Check we only count once per record
return;
}
lastRecNr=recNr;
String key = (String)this.getFieldValue("Mode");
int value = ((Integer)this.getFieldValue("Tally")).intValue();
TxSubTotal tst = subTotals.get(key);
if (tst == null){
tst = new TxSubTotal(key);
subTotals.put(key, tst);
}
tst.addTotal(value);
super.afterDetailEval();
}
//This is our datasource
public JRRewindableDataSource getSubTotalDataSource(){
List<TxSubTotal> txList = new ArrayList<>();
//add some sorting to show the power of scriplet
txList.addAll(subTotals.values());
Collections.sort(txList);
return new JRBeanCollectionDataSource(txList);
}
}
这保留了数据并实现Comparable
以进行一些不错的排序
public class TxSubTotal implements Comparable<TxSubTotal>{
private String mode;
private int total;
public TxSubTotal(String key) {
mode = key;
}
public String getMode() {
return mode;
}
public void setMode(String mode) {
this.mode = mode;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public void addTotal(int value){
this.total+=value;
}
@Override
public int compareTo(TxSubTotal o) {
return mode.compareTo(o.getMode());
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TxSubTotal){
return mode.equals(((TxSubTotal)obj).getMode());
}
return false;
}
@Override
public int hashCode() {
return mode.hashCode();
}
}
将scriptlet设置为report
scriptletClass="Scriplet"
此jrxml使用jr:table
组件显示数据,但也可以使用子报告。关键是在汇总带中使用它或在表/子报表组件上使用evaluationTime="report"
。数据源表达式为
<dataSourceExpression><![CDATA[$P{REPORT_SCRIPTLET}.getSubTotalDataSource()]]></dataSourceExpression>
完整的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="Jarvis" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" scriptletClass="Scriplet" uuid="d626fd77-14da-4974-8b4f-6368e913ac91">
<style name="table">
<box>
<pen lineWidth="0.25" lineColor="#000000"/>
</box>
</style>
<style name="table_TD" mode="Opaque" backcolor="#FFFFFF">
<box>
<pen lineWidth="0.5" lineColor="#000000"/>
</box>
</style>
<subDataset name="subTotals" uuid="486941af-2c8f-4ebb-8b8a-7a1dfe2ac0ad">
<field name="mode" class="java.lang.String"/>
<field name="total" class="java.lang.Integer"/>
</subDataset>
<queryString>
<![CDATA[]]>
</queryString>
<field name="Name" class="java.lang.String"/>
<field name="Mode" class="java.lang.String"/>
<field name="Tally" class="java.lang.Integer"/>
<background>
<band splitType="Stretch"/>
</background>
<columnHeader>
<band height="20" splitType="Stretch">
<staticText>
<reportElement mode="Opaque" x="0" y="0" width="100" height="20" forecolor="#000000" backcolor="#CCCCCC" uuid="30cd16d8-6765-42f9-b5f9-22bc0625adca"/>
<box leftPadding="2" rightPadding="2">
<pen lineWidth="0.25"/>
<topPen lineWidth="0.25"/>
<leftPen lineWidth="0.25"/>
<bottomPen lineWidth="0.25"/>
<rightPen lineWidth="0.25"/>
</box>
<textElement verticalAlignment="Middle"/>
<text><![CDATA[Name]]></text>
</staticText>
<staticText>
<reportElement mode="Opaque" x="100" y="0" width="100" height="20" forecolor="#000000" backcolor="#CCCCCC" uuid="88418f9a-5d2b-487e-b04d-e11bc57726d7"/>
<box leftPadding="2" rightPadding="2">
<pen lineWidth="0.25"/>
<topPen lineWidth="0.25"/>
<leftPen lineWidth="0.25"/>
<bottomPen lineWidth="0.25"/>
<rightPen lineWidth="0.25"/>
</box>
<textElement verticalAlignment="Middle"/>
<text><![CDATA[Mode]]></text>
</staticText>
<staticText>
<reportElement mode="Opaque" x="200" y="0" width="100" height="20" forecolor="#000000" backcolor="#CCCCCC" uuid="dd2495bb-3a3d-4b0e-b986-055bf55cf19b"/>
<box leftPadding="2" rightPadding="2">
<pen lineWidth="0.25"/>
<topPen lineWidth="0.25"/>
<leftPen lineWidth="0.25"/>
<bottomPen lineWidth="0.25"/>
<rightPen lineWidth="0.25"/>
</box>
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<text><![CDATA[Tally]]></text>
</staticText>
</band>
</columnHeader>
<detail>
<band height="20" splitType="Stretch">
<textField>
<reportElement x="0" y="0" width="100" height="20" uuid="53931171-df80-43e7-9b9d-5d77ce4223dc"/>
<box leftPadding="2" rightPadding="2">
<pen lineWidth="0.25"/>
<topPen lineWidth="0.25"/>
<leftPen lineWidth="0.25"/>
<bottomPen lineWidth="0.25"/>
<rightPen lineWidth="0.25"/>
</box>
<textElement verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{Name}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="100" y="0" width="100" height="20" uuid="f809c675-c68b-4ad0-b662-23ba6e9dd70c"/>
<box leftPadding="2" rightPadding="2">
<pen lineWidth="0.25"/>
<topPen lineWidth="0.25"/>
<leftPen lineWidth="0.25"/>
<bottomPen lineWidth="0.25"/>
<rightPen lineWidth="0.25"/>
</box>
<textElement verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{Mode}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="200" y="0" width="100" height="20" uuid="0852ff01-aa61-4a7f-9c11-a481b78b56fd"/>
<box leftPadding="2" rightPadding="2">
<pen lineWidth="0.25"/>
<topPen lineWidth="0.25"/>
<leftPen lineWidth="0.25"/>
<bottomPen lineWidth="0.25"/>
<rightPen lineWidth="0.25"/>
</box>
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{Tally}]]></textFieldExpression>
</textField>
</band>
</detail>
<summary>
<band height="75" splitType="Stretch">
<componentElement>
<reportElement key="table" style="table" x="120" y="25" width="180" height="50" uuid="5158e7c3-d87d-4877-a3ab-d6a6f9a57c5a"/>
<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="subTotals" uuid="9b73e50e-4bdf-41c2-8d40-4d215fe6b6e9">
<dataSourceExpression><![CDATA[$P{REPORT_SCRIPTLET}.getSubTotalDataSource()]]></dataSourceExpression>
</datasetRun>
<jr:column width="90" uuid="cbb85338-16b3-487d-8045-6dd94c661fc0">
<jr:detailCell style="table_TD" height="30" rowSpan="1">
<textField>
<reportElement x="0" y="0" width="90" height="30" uuid="5c07b5af-9e28-4a14-a70a-3789bfc88087"/>
<box leftPadding="2" rightPadding="2"/>
<textElement verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{mode}]]></textFieldExpression>
</textField>
</jr:detailCell>
</jr:column>
<jr:column width="90" uuid="d69567f0-ec7c-4b83-bc22-5aae1cab7879">
<jr:detailCell style="table_TD" height="30" rowSpan="1">
<textField>
<reportElement x="0" y="0" width="90" height="30" uuid="2a3a2055-f9c0-46e3-b816-b6ee1c989f9c"/>
<box leftPadding="2" rightPadding="2"/>
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression><![CDATA[$F{total}]]></textFieldExpression>
</textField>
</jr:detailCell>
</jr:column>
</jr:table>
</componentElement>
<staticText>
<reportElement x="120" y="5" width="180" height="20" uuid="9ef22e85-34ce-47c9-afad-2813a8a0b863"/>
<textElement verticalAlignment="Middle"/>
<text><![CDATA[Tx SubTotals]]></text>
</staticText>
</band>
</summary>
</jasperReport>
public static void main(String[] args) throws JRException, FileNotFoundException {
JasperReport report = JasperCompileManager.compileReport("myReport.jrxml");
JRCsvDataSource datasource = new JRCsvDataSource(new File("data/datasource.csv"));
datasource.setFieldDelimiter(';');
datasource.setUseFirstRowAsHeader(true);
datasource.setLocale(Locale.US);
JasperPrint jasperPrint = JasperFillManager.fillReport(report, new HashMap<String, Object>(), datasource);
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput("pdf/example.pdf"));
SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
exporter.setConfiguration(configuration);
exporter.exportReport();
}
答案 1 :(得分:2)
一般解决方案如下。键和值列元组的名称在子报表中设置为参数。主报告包含scriptlet,子报告和总计摘要页面。这在有许多几乎相同的子报表的情况下很有用,但只有部分子报表需要基于列元组的总计。
每个具有总元组总数的子报表必须为关键参数定义一个值,并为值参数定义值:SCRIPTLET_KEY_COLUMN_NAME
和SCRIPTLET_VALUE_COLUMN_NAME
,分别。如果未在子报表中设置key参数,则isSubreport()
将返回false,并且不会执行汇总。
主报告运行以下scriptlet,通过将 Scriptlet类设置为com.company.jasper.TupleSumScriptlet
来配置。
package com.company.jasper;
import java.util.*;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
public class TupleSumScriplet extends JRDefaultScriptlet {
private final static String REPORT_KEY_COLUMN_NAME
= "SCRIPTLET_KEY_COLUMN_NAME";
private final static String REPORT_VALUE_COLUMN_NAME
= "SCRIPTLET_VALUE_COLUMN_NAME";
private final Map<String, Integer> sums = new HashMap<>();
public TupleSumScriplet() { }
@Override
public void afterDetailEval() throws JRScriptletException {
if (isSubreport()) {
final String keyColumnName = getKeyColumnName();
final String key = (String) getFieldValue(keyColumnName);
final String valueColumnName = getValueColumnName();
final int value = (Integer) getFieldValue(valueColumnName);
final Map<String, Integer> totals = getSums();
final int sum = totals.containsKey(key) ? totals.get(key) : 0;
totals.put(key, sum + value);
}
}
public JRDataSource getDataSource() {
return new JRBeanCollectionDataSource(sort(getSums()));
}
public Map<String, Integer> getSums() {
return this.sums;
}
private String getKeyColumnName() throws JRScriptletException {
return (String) getParameterValue(REPORT_KEY_COLUMN_NAME);
}
protected String getValueColumnName() throws JRScriptletException {
return (String) getParameterValue(REPORT_VALUE_COLUMN_NAME);
}
private boolean isSubreport() {
boolean result;
try {
result = true;
final String unused = getKeyColumnName();
} catch (JRScriptletException e) {
result = false;
}
return result;
}
public static
<K extends Comparable<? super K>, V> Collection<Map.Entry<K, V>>
sort(Map<K, V> map) {
final List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
@Override
public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
return (o1.getKey()).compareTo(o2.getKey());
}
});
return list;
}
}
主报告的JRXML包括:
详细信息区子报告元素必须使用主报告中的REPORT_SCRIPLET
传入$P{REPORT_SCRIPTLET}
参数。
摘要频段子报表元素(Grand Totals)非常简单,因为它使用Map.EntrySet<K, V>
定义的API,它公开了getKey
和getValue
方法。这些方法直接映射到在子报表中定义和使用的字段 - 分别为key
和value
。该元素还必须将数据源表达式设置为:
$P{REPORT_SCRIPTLET}.getDataSource()
Grand Totals子报告的相关JRXML如下:
<field name="key" class="java.lang.String">
<fieldDescription><![CDATA[key]]></fieldDescription>
</field>
<field name="value" class="java.lang.Integer">
<fieldDescription><![CDATA[value]]></fieldDescription>
</field>
<detail>
<band height="15" splitType="Stretch">
<property name="com.jaspersoft.studio.unit.height" value="pixel"/>
<textField isBlankWhenNull="true">
<reportElement x="0" y="0" width="75" height="15" isRemoveLineWhenBlank="true"/>
<textFieldExpression><![CDATA[$F{key}]]></textFieldExpression>
</textField>
<textField isBlankWhenNull="true">
<reportElement x="75" y="0" width="150" height="15" isRemoveLineWhenBlank="true"/>
<textFieldExpression><![CDATA[$F{value}]]></textFieldExpression>
</textField>
</band>
</detail>
这会产生所需的结果,并且在元组的列名称发生更改时不需要修改Java源代码。