null

Интеграция JasperReports в проект на Spring Boot и JSF (PrimeFaces)

Исходные данные

Java 8 или выше

PrimeFaces 8.0 или выше

Maven 3.6.3 или выше

JasperReports Library 6.17.0

JasperSoft Studio 6.17.0

Немного слов о JasperReports

Библиотека JasperReports (The JasperReports Library) — это самый популярный в мире движок с открытым исходным кодом, предназначенный для генерирования отчетов. Библиотека полностью написана на языке Java и способна использовать данные, поступающие из любого источника, и создавать документы с превосходным качеством, которые можно просматривать, печатать или экспортировать в различные форматы, включая HTML, PDF, Excel, OpenOffice и Word.

JasperSoft Studio – это дизайнер отчетов JasperReports, созданный на базе Eclipse, с открытым исходным кодом. Позволяет создавать шаблоны отчетов и сами отчеты из любого источника данных, форматировать внешний вид отчетов (для печати или чтения на экране), экспортировать отчеты в широкий спектр форматов.

Подключение JasperReports в проект

Для того, чтобы использовать JasperReports Library (https://community.jaspersoft.com/project/jasperreports-library) в вашем проекте, добавьте в pom.xml вашего проекта следующую зависимость (dependency):

<dependencies>


<!-- https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports -->
<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports</artifactId>
    <version>6.17.0</version>
</dependency>


</dependencies>

Для Gradle объявление будет таким:

// https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports

implementation group: 'net.sf.jasperreports', name: 'jasperreports', version: '6.17.0'

JasperSoft Studio 6.17.0 является внешним исполняемым приложением и устанавливается отдельно (https://community.jaspersoft.com/project/jaspersoft-studio). Программа доступна для всех популярных операционных систем: Linux, MacOS, Windows.

P.S. Для скачивания необходима регистрация в JasperSoft Community (это недолго).

Настройка отчета

Под настройкой отчета в данной статье понимается следующее:

1) написание Java класса для отображения данных модели предметной области на поля отчета (mapping)

2) создание шаблона отчета

3) связывание полей шаблона с соответствующими полями класса

Для начала создадим и напишем Java класс.

Для примера, предположим, что мы разрабатываем систему управления образовательным веб-приложением. Заказчик системы попросил реализовать следующую возможность: пользователь-администратор нажимает на кнопку в веб-интерфейсе и получает отчёт «об успеваемости учащихся на курсах». Отчет должен выгружаться в формате xlsx

И так, наша предметная область – это курсы, учащиеся на них и их успеваемость.

Класс, который описывает содержимое требуемого отчета, может, к примеру выглядеть так:

import lombok.*;
import java.io.Serializable;
import java.util.Date;

@Getter
@Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor 
public class ListenersGradeProgress implements Serializable {
    /**
     * Идентификатор курса
     */
    private Long courseId;

    /**
     * Полное название курса
     */
    private String courseTitle;

    /**
     * Дата записи слушателя (обучающегося) на курс
     */
    private Date enrollmentDate;

    /**
     * Полное имя (ФИО) слушателя (обучающегося)
     */
    private String listenerFullName;

    /**
     * Email слушателя (обучающегося)
     */
    private String listenerEmail;

    /**
     * Успеваемость слушателя (обучающегося) в процентах (или баллах)
     */
    private Double gradeProgressInPercent;
}

Следующий шаг – это создание шаблона отчета.

Шаблон отчета – это файл в формате xml (имеет расширение jrxml).

Существует несколько способов создания шаблонов: с помощью дизайнера отчетов, ручное составление файлов jrxml, а также посредством программного метода (object model of the JasperReports API).

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

Приведем лишь результат создания шаблона отчета для нашего примера.

Так шаблон выглядит в дизайнере отчетов.

А ниже приведена разметка xml, которую сгенерировал дизайнер отчета.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.17.0.final using JasperReports Library version 6.17.0-6d93193241dd8cc42629e188b94f9e0bc5722efd  -->
<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="for_article" pageWidth="800" pageHeight="842" whenNoDataType="NoDataSection" columnWidth="790" leftMargin="5" rightMargin="5" topMargin="5" bottomMargin="5" isIgnorePagination="true" uuid="d453672e-0023-4314-8ef7-c80264b95e99">
	<property name="com.jaspersoft.studio.report.description" value=""/>
	<style name="ClmHdr" mode="Opaque" forecolor="#FFFFFF" backcolor="#595959" fill="Solid" hTextAlign="Center" vTextAlign="Middle" isBlankWhenNull="true" fontName="SansSerif" fontSize="10" isBold="true"/>
	<style name="Dtls" isDefault="true" mode="Transparent" hTextAlign="Center" vTextAlign="Middle" isBlankWhenNull="true" fontSize="11"/>
	<style name="NoData" hTextAlign="Center" vTextAlign="Middle" isBlankWhenNull="true" fontSize="19"/>
	<field name="courseId" class="java.lang.Long">
		<fieldDescription><![CDATA[Course identification number]]></fieldDescription>
	</field>
	<field name="courseTitle" class="java.lang.String">
		<fieldDescription><![CDATA[Full name of a course]]></fieldDescription>
	</field>
	<field name="enrollmentDate" class="java.util.Date">
		<fieldDescription><![CDATA[Listener's course enrollment date]]></fieldDescription>
	</field>
	<field name="listenerFullName" class="java.lang.String">
		<fieldDescription><![CDATA[The full name of a listener]]></fieldDescription>
	</field>
	<field name="listenerEmail" class="java.lang.String">
		<fieldDescription><![CDATA[Listener's email]]></fieldDescription>
	</field>
	<field name="gradeProgressInPercent" class="java.lang.Double">
		<fieldDescription><![CDATA[Listener's grade progress in percent]]></fieldDescription>
	</field>
	<background>
		<band splitType="Stretch"/>
	</background>
	<columnHeader>
		<band height="55" splitType="Stretch">
			<property name="com.jaspersoft.studio.layout" value="com.jaspersoft.studio.editor.layout.grid.JSSGridBagLayout"/>
			<textField>
				<reportElement key="courseIdHdr" style="ClmHdr" isPrintRepeatedValues="false" x="0" y="0" width="132" height="55" uuid="a8e7fd9f-5da2-45a1-a703-275f1786fee4">
					<property name="com.jaspersoft.studio.unit.width" value="px"/>
					<property name="com.jaspersoft.studio.element.name" value="id-hdr"/>
				</reportElement>
				<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="10" isBold="true"/>
				</textElement>
				<textFieldExpression><![CDATA["ID курса"]]></textFieldExpression>
			</textField>
			<textField>
				<reportElement key="courseTitleHdr" style="ClmHdr" isPrintRepeatedValues="false" x="132" y="0" width="132" height="55" uuid="373c6e21-c5ef-4041-9cd2-f4810228adc8">
					<property name="com.jaspersoft.studio.unit.width" value="px"/>
					<property name="com.jaspersoft.studio.unit.x" value="px"/>
					<property name="com.jaspersoft.studio.element.name" value="course-hdr"/>
				</reportElement>
				<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="10" isBold="true"/>
				</textElement>
				<textFieldExpression><![CDATA["Название курса"]]></textFieldExpression>
			</textField>
			<textField>
				<reportElement key="enrollmentDateHdr" style="ClmHdr" isPrintRepeatedValues="false" x="264" y="0" width="132" height="55" uuid="165e7f35-7bd8-448a-8642-325952bbc54f">
					<property name="com.jaspersoft.studio.unit.width" value="px"/>
					<property name="com.jaspersoft.studio.unit.x" value="px"/>
					<property name="com.jaspersoft.studio.element.name" value="date-hdr"/>
				</reportElement>
				<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="10" isBold="true"/>
				</textElement>
				<textFieldExpression><![CDATA["Дата записи на курс"]]></textFieldExpression>
			</textField>
			<textField>
				<reportElement key="listenerFullnameHdr" style="ClmHdr" isPrintRepeatedValues="false" x="396" y="0" width="132" height="55" uuid="c0825ef5-5b9b-4be0-8aa8-91e5baf4732c">
					<property name="com.jaspersoft.studio.unit.width" value="px"/>
					<property name="com.jaspersoft.studio.unit.x" value="px"/>
					<property name="com.jaspersoft.studio.element.name" value="listener-hdr"/>
				</reportElement>
				<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="10" isBold="true"/>
				</textElement>
				<textFieldExpression><![CDATA["Обучающийся"]]></textFieldExpression>
			</textField>
			<textField>
				<reportElement key="listenerEmailHdr" style="ClmHdr" isPrintRepeatedValues="false" x="528" y="0" width="131" height="55" uuid="76fc1ac4-8da1-4f95-b9c9-5983c48a498e">
					<property name="com.jaspersoft.studio.unit.width" value="px"/>
					<property name="com.jaspersoft.studio.unit.x" value="px"/>
					<property name="com.jaspersoft.studio.element.name" value="email-hdr"/>
				</reportElement>
				<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="10" isBold="true"/>
				</textElement>
				<textFieldExpression><![CDATA["Email"]]></textFieldExpression>
			</textField>
			<textField>
				<reportElement key="listenerGradeProgressHdr" style="ClmHdr" isPrintRepeatedValues="false" x="659" y="0" width="131" height="55" uuid="33922d22-5cb3-468f-a847-7de7f3c5df8b">
					<property name="com.jaspersoft.studio.unit.width" value="px"/>
					<property name="com.jaspersoft.studio.unit.x" value="px"/>
					<property name="com.jaspersoft.studio.element.name" value="progress-hdr"/>
				</reportElement>
				<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="10" isBold="true"/>
				</textElement>
				<textFieldExpression><![CDATA["Уровень освоения (баллы)"]]></textFieldExpression>
			</textField>
		</band>
	</columnHeader>
	<detail>
		<band height="50" splitType="Stretch">
			<property name="com.jaspersoft.studio.unit.height" value="px"/>
			<property name="com.jaspersoft.studio.layout" value="com.jaspersoft.studio.editor.layout.grid.JSSGridBagLayout"/>
			<textField textAdjust="StretchHeight" isBlankWhenNull="true">
				<reportElement key="courseId" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="0" y="0" width="132" height="50" backcolor="#FFFFFF" uuid="e01cae5a-1224-4d35-a671-dc24d0dc2e79">
					<property name="com.jaspersoft.studio.element.name" value="id-dtl"/>
				</reportElement>
				<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="11"/>
				</textElement>
				<textFieldExpression><![CDATA[$F{courseId}]]></textFieldExpression>
			</textField>
			<textField textAdjust="StretchHeight" isBlankWhenNull="true">
				<reportElement key="courseTitle" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="132" y="0" width="132" height="50" uuid="e099966b-6e68-4bc5-9ac3-28d92d67275f">
					<property name="com.jaspersoft.studio.element.name" value="course-dtl"/>
				</reportElement>
				<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="11"/>
				</textElement>
				<textFieldExpression><![CDATA[$F{courseTitle}]]></textFieldExpression>
			</textField>
			<textField textAdjust="StretchHeight" isBlankWhenNull="true">
				<reportElement key="enrollmentDate" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="264" y="0" width="132" height="50" uuid="7144ea06-3947-4ead-8b61-e6ec4a1605b1">
					<property name="com.jaspersoft.studio.element.name" value="date-dtl"/>
				</reportElement>
				<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="11"/>
				</textElement>
				<textFieldExpression><![CDATA[$F{enrollmentDate}]]></textFieldExpression>
			</textField>
			<textField textAdjust="StretchHeight" isBlankWhenNull="true">
				<reportElement key="listenerFullName" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="396" y="0" width="132" height="50" uuid="e86e802b-9a3a-41e6-a38b-b2a74315940b">
					<property name="com.jaspersoft.studio.element.name" value="listener-dtl"/>
				</reportElement>
				<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="11"/>
				</textElement>
				<textFieldExpression><![CDATA[$F{listenerFullName}]]></textFieldExpression>
			</textField>
			<textField textAdjust="StretchHeight" isBlankWhenNull="true">
				<reportElement key="listenerEmail" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="528" y="0" width="131" height="50" uuid="86b01f71-f4de-4199-b724-a9a5357e8a47">
					<property name="com.jaspersoft.studio.element.name" value="email-dtl"/>
				</reportElement>
				<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="11"/>
				</textElement>
				<textFieldExpression><![CDATA[$F{listenerEmail}]]></textFieldExpression>
			</textField>
			<textField textAdjust="StretchHeight" isBlankWhenNull="true">
				<reportElement key="gradeProgressInPercent" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="659" y="0" width="131" height="50" uuid="63d80376-ad5d-455e-8b1c-6fa4841ce2e1">
					<property name="com.jaspersoft.studio.element.name" value="progress-dtl"/>
				</reportElement>
				<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="11"/>
				</textElement>
				<textFieldExpression><![CDATA[$F{gradeProgressInPercent}]]></textFieldExpression>
			</textField>
		</band>
	</detail>
	<noData>
		<band height="71" splitType="Stretch">
			<property name="com.jaspersoft.studio.layout" value="com.jaspersoft.studio.editor.layout.grid.JSSGridBagLayout"/>
			<staticText>
				<reportElement style="NoData" stretchType="ContainerHeight" mode="Transparent" x="0" y="0" width="790" height="71" isRemoveLineWhenBlank="true" uuid="318c618b-4af1-4ad5-a121-31a97904df7e"/>
				<textElement textAlignment="Center" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="19"/>
				</textElement>
				<text><![CDATA[Для данного курса нет зарегистрированных слушателей]]></text>
			</staticText>
		</band>
	</noData>
</jasperReport>

Немного слов о связывании полей шаблона с соответствующими полями класса.

Рассмотрим для примера поле «Идентификатор курса» (courseId) в классе ListenersGradeProgress:

/**
* Идентификатор курса
*/
private Long courseId;


public Long getCourseId(){
return courseId;
}

Обратите внимание на тип поля – Long.

Для того, чтобы связывание этого поля с соответствующим полем в отчете (field):

<field name="courseId" class="java.lang.Long">
		<fieldDescription><![CDATA[Course identification number]]></fieldDescription>
</field>

 произошло успешно, необходимо выполнить следующие условия:

1) типы полей должны совпадать (в нашем случае Long).

2) названия* полей должны совпадать.

* Имеется ввиду, что если к примеру field отчета имеет название courseId, то в таком случае, в Java классе должен присутствовать соответствующий getter, наименование которого соответствует стандартам наименования Java: getCourseId().

Для отображения значения поля на экран, в JasperReports могут использоваться различные элементы отчета. В нашем примере все значения выводятся с помощью элементов Text Field.

<textField textAdjust="StretchHeight" isBlankWhenNull="true">
				<reportElement key="courseId" style="Dtls" stretchType="ContainerHeight" mode="Transparent" x="0" y="0" width="132" height="50" backcolor="#FFFFFF" uuid="e01cae5a-1224-4d35-a671-dc24d0dc2e79">
					<property name="com.jaspersoft.studio.element.name" value="id-dtl"/>
				</reportElement>
				<textElement textAlignment="Justified" verticalAlignment="Middle" markup="styled">
					<font fontName="SansSerif" size="11"/>
				</textElement>
				<textFieldExpression><![CDATA[$F{courseId}]]></textFieldExpression>
</textField>

Обратите внимание на место, где происходит связывание text field с field отчета:

<textFieldExpression><![CDATA[$F{courseId}]]></textFieldExpression>

В дизайнере отчетов эта связь задается с помощью редактора выражений.

Генерирование отчета

Следующий шаг интеграции – это непосредственно само генерирование отчета.

Создадим простой сервис для работы с отчетами. Мы будем обращаться к его методам из веб-интерфейса.

import org.springframework.stereotype.Service;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import net.sf.jasperreports.export.SimpleXlsxReportConfiguration;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;

@Slf4j
@Service
public class JasperReportsService {


public InputStream getReportAsStream() {

        InputStream s;

        try {

// В данном примере, путь к файлу шаблона отчета: src/main/resources/reports.jasper/..
            s = getClass().getResourceAsStream("/reports.jasper/for_article.jrxml");
        } 
     catch (Exception e) {
            log.error("[Jasper Reports Service. Get Report As Stream]: " +
                            "Error occurred while getting resource (report jrxml file) as stream. " +
                            "Details: {}",
                    ExceptionUtils.getStackTrace(e));
            s = null;
        }

        return s;
    }



public StreamedContent generateListenersGradeProgressReport()
 {

        DefaultStreamedContent sc = DefaultStreamedContent.builder().build();

        JasperReport r;
        JasperPrint jp;

        try {
            r = JasperCompileManager.compileReport(getReportAsStream());
        } catch (Exception e) {
            log.error(
                           "[Jasper Reports Service. Generate Listeners Grade Progress Report]: " +
                            "Error occurred while compiling the report. " +
                            "Details: {}",
                    ExceptionUtils.getStackTrace(e));
            return sc;
        }

//Метод findListenersGradeProgressReportData возвращает данные по успеваемости обучающихся из хранилища данных. 
//Реализация метода опущена.

        List<ListenersGradeProgress> reportData = findListenersGradeProgressReportData();

        if (reportData == null) {
            return sc;
        }

//Второй параметр выставляем в false. 
//Это означает, что мы не хотим, чтобы описание полей (тег <fieldDescription>)
//в шаблоне отчета (файл .jrxml) использовалось в качестве синонимов полей отчета 
//при отображении их на поля Java класса, описывающего предметную область. 
 
        var s = new JRBeanCollectionDataSource(reportData, false);

        var p = new HashMap<String, Object>();

//Устанавливаем этот параметр в true, чтобы информация не разбивалась на страницы (листы в электронных таблицах),
//а оставалась на одной единственной странице (листе)
        p.put(JRParameter.IS_IGNORE_PAGINATION, true);

        p.put(JRParameter.REPORT_LOCALE, new Locale("ru", "RU"));

        try {
            jp = JasperFillManager.fillReport(r, p, s);
        } catch (Exception e) {
            log.error("[Jasper Reports Service. Generate Listeners Grade Progress Report]: " +
                            "Error occurred while filling the report. " +
                            "Details: {}",
                    ExceptionUtils.getStackTrace(e));
            return sc;
        }


                var os = new ByteArrayOutputStream();

                var exp = new JRXlsxExporter();
                var conf = new SimpleXlsxReportConfiguration();

                conf.setRemoveEmptySpaceBetweenRows(true);
                conf.setRemoveEmptySpaceBetweenColumns(true);
                conf.setSheetNames(new String[]{"Успеваемость слушателей"});
                conf.setDetectCellType(true);
                conf.setWrapText(true);
                conf.setCollapseRowSpan(false);
                conf.setWhitePageBackground(false);
                conf.setFreezeRow(2);
                exp.setConfiguration(conf);
                exp.setExporterInput(new SimpleExporterInput(jp));
                exp.setExporterOutput(new SimpleOutputStreamExporterOutput(os));
                try {
                    exp.exportReport();

                    sc = DefaultStreamedContent.builder()
                            .name("название_отчета.xlsx")
                            .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
                            .contentLength(os.size())
                            .stream(()
                                    -> new ByteArrayInputStream(os.toByteArray())
                            )
                            .build();

                } catch (Exception e) {
                    log.error("[Jasper Reports Service. Generate Listeners Grade Progress Report]: " +
                                    "Error occurred while exporting the report. " +
                                    "Details: {}",
                            ExceptionUtils.getStackTrace(e));
                }
        return sc;
    }

}

Теперь напишем класс, который будет доступен для JSF страницы, и который будет обрабатывать запрос от пользователя на формирование отчета.

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.context.annotation.Scope;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import <your services package path> .JasperReportsService;

@Slf4j
@Scope("view")
@Component
@Getter
@Setter
public class AdminCoursesManagementBean{

    @Autowired
    private JasperReportsService jasperReportsService;

public StreamedContent downloadListenersGradeProgressReport() {
        StreamedContent c = DefaultStreamedContent.builder().build();
        
        try {
           //Вызываем метод, созданного нами сервиса
            c = jasperReportsService.generateListenersGradeProgressReport();
        } catch (Exception e) {
            log.error("[Admin Courses Management. Download Listeners Grade Progress Report]: " +
                            "Error occurred while generating the report. " +
                            "Details: {}",
                    ExceptionUtils.getStackTrace(e));
        }
        if(c.getStream() == null){
            log.error("[Admin Courses Management. Download Listeners Grade Progress Report]: " +
                            "Error occurred while downloading the report. " +
                            "Details: Streamed content is null!");
        }
        return c;
    }

}

Наконец, напишем разметку JSF страницы (.xhtml) (для краткости приведен лишь участок разметки, реализующий нужный нам функционал):

    <p:menuButton value="Операции">
     <p:menuitem value="Операция такая-то" action="some action here"
              icon="some icon here"/>
                            
      <p:menuitem  ajax="false" immediate="true">
          <i class="fa fa-file-excel-o" style="display: table-cell;
                    vertical-align: middle;"/>
           <p:outputLabel  style="display: table-cell; cursor: pointer;" value="Скачать отчет по успеваемости"/>
          <p:fileDownload value="#{ adminCoursesManagementBean.downloadListenersGradeProgressReport()}"/>
          </p:menuitem>
     </p:menuButton>

Результатом работы написанного функционала может быть подобный отчет:

Нюансы

Для экспортирования отчета в формат xlsx мы использовали класс JRXlsxExporter из библиотеки JasperReports.

Существуют некоторые нюансы при работе с данным экспортером, которые необходимо учитывать, чтобы результирующий файл xlsx полностью без искажений соответствовал созданному нами шаблону отчета.

1) Для отображения статического текста (например в заголовках столбцов – раздел Column Header в шаблоне), а также для отображения значений полей отчета (раздел Detail в шаблоне) используйте исключительно элементы Text fields. Не используйте элементы static text. В противном случае, в результирующем файле xlsx данные выведены не будут.

Можно использовать элемент Static text в разделе шаблона No Data.

Коротко об этом разделе:

если результатом выполнения следующего кода:

var s = new JRBeanCollectionDataSource(reportData, false);

будет пустой источник данных и, такое возможно, если reportData является пустой коллекцией (запрос в базу данных вернул отсутствие данных об успеваемости), то в выгружаемом файле будет отображаться именно содержимое раздела No Data, в то время как другие разделы (Column Header и Detail) выведены не будут.

2) Необходимо и рекомендовано использовать универсальные шрифты в шаблоне отчета. Под универсальностью подразумевается гарантированное наличие такого шрифта в любой из следующих операционных систем одновременно: Linux, MacOS, Windows.

К таким шрифтам, к примеру, относится SansSerif.

Не соблюдение этого правила приведет к следующему:

1) Если при разработке шаблона Вы использовали шрифт, который есть в вашей операционной системе, и он не относится к универсальному, то при использовании вашего шаблона приложением, генерирующим отчет, на другой операционной системе или на машине, где нет такого шрифта, движок Jasper Reports выдаст критическое исключение (Exception) и процесс генерирования отчета будет невозможным.

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

Заключение

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

Ее интеграция в проект достаточна проста и не требует больших затрат времени.

Библиотека постоянно обновляется, поддерживается и бесплатна для коммерческого использования.

Если вы разрабатываете приложение на Java и/или Kotlin, то данная библиотека будет хорошим выбором и может стать вашим любимым инструментом для генерирования отчетности.

Назад