Перейти к основному содержимому

JasperReports с Spring

· 5 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

1. Обзор

JasperReports — это библиотека отчетов с открытым исходным кодом, которая позволяет пользователям создавать точные отчеты с точностью до пикселя, которые можно распечатать или экспортировать во многих форматах, включая PDF, HTML и XLS.

В этой статье мы рассмотрим его ключевые функции и классы, а также реализуем примеры, чтобы продемонстрировать его возможности.

2. Зависимость от Maven

Во-первых, нам нужно добавить зависимость jasperreports к нашему pom.xml :

<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.4.0</version>
</dependency>

Последнюю версию этого артефакта можно найти здесь .

3. Шаблоны отчетов

Дизайн отчетов определяется в файлах JRXML. Это обычные файлы XML с определенной структурой, которую может интерпретировать движок JasperReports.

Давайте теперь посмотрим только на соответствующую структуру файлов JRXML, чтобы лучше понять Java-часть процесса генерации отчетов, которая является нашей основной задачей.

Давайте создадим простой отчет для отображения информации о сотрудниках:

<jasperReport ... >
<field name="FIRST_NAME" class="java.lang.String"/>
<field name="LAST_NAME" class="java.lang.String"/>
<field name="SALARY" class="java.lang.Double"/>
<field name="ID" class="java.lang.Integer"/>
<detail>
<band height="51" splitType="Stretch">
<textField>
<reportElement x="0" y="0" width="100" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{FIRST_NAME}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="100" y="0" width="100" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{LAST_NAME}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="200" y="0" width="100" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{SALARY}]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>

3.1. Составление отчетов

Файлы JRXML необходимо скомпилировать, чтобы механизм отчетов мог заполнить их данными.

Выполним эту операцию с помощью класса JasperCompilerManager :

InputStream employeeReportStream
= getClass().getResourceAsStream("/employeeReport.jrxml");
JasperReport jasperReport
= JasperCompileManager.compileReport(employeeReportStream);

Чтобы не компилировать его каждый раз, мы можем сохранить его в файл:

JRSaver.saveObject(jasperReport, "employeeReport.jasper");

4. Заполнение отчетов

Наиболее распространенный способ заполнения составленных отчетов — записями из базы данных. Для этого требуется, чтобы отчет содержал SQL-запрос, который механизм будет выполнять для получения данных.

Во-первых, давайте изменим наш отчет, чтобы добавить SQL-запрос:

<jasperReport ... >
<queryString>
<![CDATA[SELECT * FROM EMPLOYEE]]>
</queryString>
...
</jasperReport>

Теперь давайте создадим простой источник данных:

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:employee-schema.sql")
.build();
}

Теперь мы можем заполнить отчет:

JasperPrint jasperPrint = JasperFillManager.fillReport(
jasperReport, null, dataSource.getConnection());

Обратите внимание, что мы передаем null во второй аргумент, так как наш отчет еще не получает никаких параметров.

4.1. Параметры

Параметры полезны для передачи обработчику отчетов данных, которые он не может найти в своем источнике данных, или когда данные изменяются в зависимости от различных условий выполнения.

Мы также можем изменить части или даже весь SQL-запрос с параметрами, полученными в операции заполнения отчета.

Для начала изменим отчет, чтобы он получал три параметра:

<jasperReport ... >
<parameter name="title" class="java.lang.String" />
<parameter name="minSalary" class="java.lang.Double" />
<parameter name="condition" class="java.lang.String">
<defaultValueExpression>
<![CDATA["1 = 1"]]></defaultValueExpression>
</parameter>
// ...
</jasperreport>

Теперь давайте добавим раздел title для отображения параметра title :

<jasperreport ... >
// ...
<title>
<band height="20" splitType="Stretch">
<textField>
<reportElement x="238" y="0" width="100" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String">
<![CDATA[$P{title}]]></textFieldExpression>
</textField>
</band>
</title>
...
</jasperreport/>

Затем давайте изменим запрос, чтобы использовать параметры minSalary и условия :

SELECT * FROM EMPLOYEE
WHERE SALARY >= $P{minSalary} AND $P!{condition}

Обратите внимание на другой синтаксис при использовании параметра условия . Это сообщает обработчику, что параметр не должен использоваться как стандартный параметр PreparedStatement , а как если бы значение этого параметра было записано изначально в SQL-запросе.

Наконец, подготовим параметры и заполним отчет:

Map<String, Object> parameters = new HashMap<>();
parameters.put("title", "Employee Report");
parameters.put("minSalary", 15000.0);
parameters.put("condition", " LAST_NAME ='Smith' ORDER BY FIRST_NAME");

JasperPrint jasperPrint
= JasperFillManager.fillReport(..., parameters, ...);

Обратите внимание, что ключи параметров соответствуют именам параметров в отчете. Если механизм обнаруживает, что параметр отсутствует, он получит значение из defaultValueExpression параметра, если таковой имеется.

5. Экспорт

Чтобы экспортировать отчет, мы сначала создаем объект класса экспортера, который соответствует нужному нам формату файла.

Затем мы устанавливаем наш предыдущий заполненный отчет в качестве входных данных и определяем, куда выводить результирующий файл.

При желании мы можем установить соответствующий отчет и объекты конфигурации экспорта, чтобы настроить процесс экспорта.

5.1. PDF

JRPdfExporter exporter = new JRPdfExporter();

exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
exporter.setExporterOutput(
new SimpleOutputStreamExporterOutput("employeeReport.pdf"));

SimplePdfReportConfiguration reportConfig
= new SimplePdfReportConfiguration();
reportConfig.setSizePageToContent(true);
reportConfig.setForceLineBreakPolicy(false);

SimplePdfExporterConfiguration exportConfig
= new SimplePdfExporterConfiguration();
exportConfig.setMetadataAuthor("foreach");
exportConfig.setEncrypted(true);
exportConfig.setAllowedPermissionsHint("PRINTING");

exporter.setConfiguration(reportConfig);
exporter.setConfiguration(exportConfig);

exporter.exportReport();

5.2. XLS

JRXlsxExporter exporter = new JRXlsxExporter();

// Set input and output ...
SimpleXlsxReportConfiguration reportConfig
= new SimpleXlsxReportConfiguration();
reportConfig.setSheetNames(new String[] { "Employee Data" });

exporter.setConfiguration(reportConfig);
exporter.exportReport();

5.3. CSV

JRCsvExporter exporter = new JRCsvExporter();

// Set input ...
exporter.setExporterOutput(
new SimpleWriterExporterOutput("employeeReport.csv"));

exporter.exportReport();

5.4. HTML

HtmlExporter exporter = new HtmlExporter();

// Set input ...
exporter.setExporterOutput(
new SimpleHtmlExporterOutput("employeeReport.html"));

exporter.exportReport();

6. Подотчеты

Вложенные отчеты — это не что иное, как стандартный отчет, встроенный в другой отчет.

Во-первых, давайте создадим отчет, чтобы показать электронные письма сотрудника:

<jasperReport ... >
<parameter name="idEmployee" class="java.lang.Integer" />
<queryString>
<![CDATA[SELECT * FROM EMAIL WHERE ID_EMPLOYEE = $P{idEmployee}]]>
</queryString>
<field name="ADDRESS" class="java.lang.String"/>
<detail>
<band height="20" splitType="Stretch">
<textField>
<reportElement x="0" y="0" width="156" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{ADDRESS}]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>

Теперь давайте изменим наш отчет о сотрудниках, включив в него предыдущий:

<detail>
<band ... >
<subreport>
<reportElement x="0" y="20" width="300" height="27"/>
<subreportParameter name="idEmployee">
<subreportParameterExpression>
<![CDATA[$F{ID}]]></subreportParameterExpression>
</subreportParameter>
<connectionExpression>
<![CDATA[$P{REPORT_CONNECTION}]]></connectionExpression>
<subreportExpression class="java.lang.String">
<![CDATA["employeeEmailReport.jasper"]]></subreportExpression>
</subreport>
</band>
</detail>

Обратите внимание, что мы ссылаемся на подотчет по имени скомпилированного файла и передаем ему idEmployee и текущее подключение к отчету в качестве параметров.

Далее скомпилируем оба отчета:

InputStream employeeReportStream
= getClass().getResourceAsStream("/employeeReport.jrxml");
JasperReport jasperReport
= JasperCompileManager.compileReport(employeeReportStream);
JRSaver.saveObject(jasperReport, "employeeReport.jasper");

InputStream emailReportStream
= getClass().getResourceAsStream("/employeeEmailReport.jrxml");
JRSaver.saveObject(
JasperCompileManager.compileReport(emailReportStream),
"employeeEmailReport.jasper");

Наш код для заполнения и экспорта отчета не требует доработок.

7. Заключение

В этой статье мы кратко рассмотрели основные функции библиотеки JasperReports.

Мы смогли составить и заполнить отчеты записями из базы данных; мы передали параметры для изменения данных, отображаемых в отчете, в соответствии с различными условиями выполнения, внедрили вложенные отчеты и экспортировали их в наиболее распространенные форматы.

Полный исходный код этой статьи можно найти на GitHub .