Бэнды и наборы данных

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

Бэнд заполняется данными, предоставляемыми одним или несколькими наборами данных, связанными с этим бэндом.

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

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

bands 1
Figure 1. Пример бэндов отчета

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

Примеры различных бэндов и наборов данных можно найти в руководстве Report Generation.

Бэнды

Вкладка Bands

Вкладка Bands редактора отчетов позволяет определить иерархию бэндов и наборы данных для каждого бэнда.

bands

Бэнд отчета имеет следующие параметры:

  • Name — имя бэнда, уникальное в пределах отчета. Должно содержать только латинские буквы, цифры и знаки подчеркивания.

    Если имя бэнда начинается с header, его данные не будут выводиться при использовании форматера таблиц.
  • Orientation - ориентация бэнда: Horizontal, Vertical или Crosstab.

    • Horizontal бэнды копируются вниз. Они могут содержать подбэнды.

    • Vertical бэнды копируются вправо.

    • Crosstab бэнды копируются вправо и вниз в виде матрицы.

  • Parent - родительский бэнд.

  • Multiple Datasets - установите этот флажок, если хотите определить более одного набора данных для бэнда.

  • Dataset type — тип набора данных.

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

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

Каждый отчет имеет бэнд Root. В нем можно создавать наборы данных и ссылаться на их поля из других бэндов.

Обратите внимание, что столбец Dataset name, доступный при работе с несколькими наборами данных, предназначен только для удобства пользователя.

Аннотация @BandDef

Аннотация @BandDef позволяет определять бэнды при создании отчета во время разработки. Она имеет следующие атрибуты:

  • name - имя бэнда, уникальное в пределах отчета. Должно содержать только латинские буквы, цифры и знаки подчеркивания.

  • parent - имя родительского бэнда. Может быть опущено только для бэнда Root.

  • root - должно быть установлено в true только для бэнда Root. По умолчанию false.

  • orientation - ориентация бэнда, определяемая перечислением Orientation: HORIZONTAL, VERTICAL, CROSS. По умолчанию HORIZONTAL.

  • dataSets - наборы данных, определяемые массивом аннотаций @DataSetDef.

Отчет должен включать определение бэнда Root с атрибутами name = "Root" и root = true.

Порядок аннотаций @BandDef в Java-классе определения отчета важен. Он влияет на порядок вывода бэндов на одном уровне иерархии.

Наборы данных

Набор данных SQL

SQL набор данных формируется в результате выполнения SQL-запроса. Рекомендуется использовать псевдонимы для полей результата запроса с помощью оператора as. Также рекомендуется заключать псевдонимы в двойные кавычки, чтобы предотвратить возможное преобразование регистра СУБД:

select u.name as "userName", u.login as "userLogin"
from user_ u

В запросе можно использовать входные параметры отчета и поля родительских бэндов. К параметрам следует обращаться по имени, заключенному в ${}, например, ${dateFrom}. К полям родительских бэндов следует обращаться аналогично, добавляя имя бэнда перед именем поля: ${band1.field1}.

Ниже приведен пример SQL-запроса с параметром groupId, полученным из родительского бэнда group, и внешним параметром enabled:

select u.name as "userName", u.login as "userLogin"
from user_ u
where u.group_id = ${group.groupId}
    and u.active = ${active}
    and u.deleted_date is null

В SQL-запросах следует вручную добавлять условия для фильтрации записей, помеченных как удаленные.

Выбор хранилища данных

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

Предварительная обработка запросов в бэндах

Если необходимо динамически модифицировать SQL/JPQL запрос в зависимости от входных параметров отчета или значений параметров из родительского бэнда, можно использовать предварительную обработку SQL. Механизм шаблонов позволяет модифицировать SQL/JPQL запросы с помощью Groovy. Для активации установите флажок Preprocess query as Groovy template под редактором бэнда. Итоговый запрос будет обработан GStringTemplateEngine, который получит доступ к:

  • параметрам отчета: ${<имя_параметра>},

  • значениям из родительских бэндов: ${<имя_бэнда>.<имя_параметра>}.

Рассмотрим пример: в зависимости от того, передан ли параметр отчета createTs2, нужно выбрать одно из условий запроса: e.create_ts < ${createTs2} или e.create_ts < current_timestamp.

В этом случае запрос должен выглядеть так:

select e.create_ts, e.id, e.vin from ref_car e
where
e.create_ts >= ${createTs1}
and
<% out << (createTs2 != null  ? 'e.create_ts < ${createTs2}' : 'e.create_ts < current_timestamp')%>

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

select e.create_ts, e.id, e.vin from ref_car e
where
e.create_ts >= ${createTs1}
and
e.create_ts < current_timestamp

Если createTs2 передан, для бэнда будет использован следующий итоговый запрос:

select e.create_ts, e.id, e.vin from ref_car e
where
e.create_ts >= ${createTs1}
and
e.create_ts < ${createTs2}

Набор данных JPQL

Набор данных JPQL формируется в результате выполнения JPQL-запроса. Поля результирующего запроса должны иметь псевдонимы, заданные с помощью оператора as. Аналогично SQL-запросу, в JPQL-запросе можно использовать входные параметры отчета и поля родительских бэндов.

Ниже приведен пример JPQL-запроса с параметром groupId, полученным из родительского бэнда group, и внешним параметром enabled:

select u.name as userName, u.login as userLogin
from User u
where u.group.id = ${group.groupId}
    and u.active = ${active}

JPQL-запросы автоматически поддерживают мягкое удаление и возвращают только записи, не помеченные как удаленные.

При использовании JPQL-запроса в качестве источника данных для отчета обратите внимание, что запрос выполняется напрямую к базе данных и обходит DataManager. Это означает, что обычные проверки безопасности доступа к данным (на уровне строк и ресурсов) не применяются автоматически. Вы должны убедиться, что ваши JPQL-запросы извлекают только те данные, к которым пользователь имеет право доступа. Вы несете ответственность за реализацию необходимых проверок безопасности в ваших запросах.

Вы также можете использовать Link field для объединения нескольких наборов данных, использовать Data store для запросов к дополнительному хранилищу данных или активировать предварительную обработку запросов, установив флажок Preprocess query as Groovy template под редактором бэнда.

Набор данных Groovy

Набор данных Groovy формируется в результате выполнения Groovy-скрипта. Скрипт возвращает объект типа List<Map<String, Object>>. Каждый элемент этого списка - объект типа Map<String, Object> - соответствует одной записи набора данных.

В скрипт передаются следующие объекты:

  • params - карта внешних параметров отчета. Пример получения значения параметра:

    def active = params['active']
  • parentBand - родительский бэнд как объект типа io.jmix.reports.yarg.structure.BandData. Этот объект позволяет получить значение поля родительского бэнда путем вызова метода getParameterValue(), например:

    def groupId = parentBand.getParameterValue('groupId')
  • currentAuthentication - объект типа io.jmix.core.security.CurrentAuthentication, связанный с текущим аутентифицированным пользователем. Например:

    def user = currentAuthentication.getUser()
  • metadata - объект типа io.jmix.core.Metadata, предоставляющий доступ к метаданным приложения. Например:

    def metaClass = metadata.getClass('User')
  • dataManager - объект типа io.jmix.core.DataManager, предоставляющий CRUD-функциональность. Например:

    def book = dataManager.load(Book.class).id(bookId).fetchPlan("book.edit").one()
  • timeSource - объект типа io.jmix.core.TimeSource, используемый для получения текущего времени. Например:

    def currentDate = timeSource.currentTimestamp()
  • applicationContext - объект типа org.springframework.context.ApplicationContext, предоставляющий доступ к управляемым бинам. Например:

    def accountService = applicationContext.getBean(AccountService);

Набор данных Entity

Набор данных Entity состоит из одной строки и формируется с использованием атрибутов одного экземпляра JPA-сущности и связанных с ней сущностей.

Источник данных формируется из внешнего параметра типа Entity, который должен быть описан на вкладке Parameters. Значение в поле Entity parameter name должно совпадать с именем параметра.

Шаблон отчета должен содержать поля с именами атрибутов сущности. Имена атрибутов могут использовать имена из существующего фетч-плана (при установке флажка Use existing fetch plan) или могут быть выбраны кнопкой Select entity attributes.

Поскольку механизм отчетов всегда перезагружает сущности в наборе данных Entity из базы данных, вы не можете использовать его для DTO-сущностей или экземпляров JPA-сущностей, не сохраненных в базе данных. Вместо этого используйте Набор данных Groovy и обращайтесь к сущности, переданной как параметр, следующим образом:

def entity = params['entity']

return [['title': entity.title,
        'description': entity.description]]

Набор данных List of Entities

Набор данных List of entities формируется с использованием списка экземпляров JPA-сущностей.

Источник данных формируется с использованием внешнего параметра типа List of entities, который должен быть описан на вкладке Parameters. Значение в поле List of entities parameter name должно совпадать с псевдонимом параметра.

Шаблон отчета должен содержать поля с именами атрибутов сущности. Имена атрибутов могут использовать имена из существующего фетч-плана (при установке флажка Use existing fetch plan) или могут быть выбраны кнопкой Select entity attributes.

Поскольку механизм отчетов всегда перезагружает сущности в наборе данных List of entities из базы данных, вы не можете использовать его для DTO-сущностей или экземпляров JPA-сущностей, не сохраненных в базе данных. Вместо этого используйте Набор данных Groovy и обращайтесь к списку сущностей, переданному как параметр, следующим образом:

return params['entities'].collect {[
    'title': it.title,
    'description': it.description
]}

Набор данных JSON

Набор данных JSON генерируется из JSON-данных. Эти данные могут быть получены из следующих источников:

  1. Groovy-скрипт

    Предполагается, что скрипт, предоставленный пользователем, возвращает JSON-данные в виде строки.

    Например:

    return ''' 
            {
              "items": [
                {
                  "name": "Java Concurrency in practice",
                  "price": 15000
                },
                {
                  "name": "Clear code",
                  "price": 13000
                },
                {
                  "name": "Scala in action",
                  "price": 12000
                }
              ]
            }
            '''
  2. URL

    Механизм отчетов выполнит HTTP-запрос GET по указанному URL.

    Например:

    https://jsonplaceholder.typicode.com/users
  3. Parameter

    Внешний параметр отчета типа String, содержащий JSON-данные, должен быть описан на вкладке Parameters.

Полученное JSON-дерево запрашивается с помощью JsonPath-запроса. Например, можно использовать JsonPath $.store.book[*] для возврата всех книг из следующего JSON-дерева:

{
    "store": {
    "book": [
            {
                "category": "reference",
                "author": "Nigel Rees",
                "title": "Sayings of the Century",
                "price": 8.95
            },
            {
                "category": "fiction",
                "author": "Evelyn Waugh",
                "title": "Sword of Honour",
                "price": 12.99,
                "isbn": "0-553-21311-3"
            }
    ],
    "bicycle": {
        "color": "red",
        "price": 19.95
    }
}
}

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

Поля, выводимые отчетом, которые имеют типы данных Date, DateTime или Time, не поддерживают формат, определенный java.text.SimpleDateFormat. Чтобы установить правильный формат, следует написать Groovy-скрипт.

Для этого перейдите на вкладку Value formats экрана деталей отчета и откройте диалог форматера. Например, для поля bookPublication.dateTime Groovy-скрипт будет выглядеть так:

import java.text.SimpleDateFormat

def simpleOldDateFormat = new SimpleDateFormat('yyyy-MM-dd HH:mm')
def simpleNewDateFormat = new SimpleDateFormat('dd/MM/yyyy HH:mm')
def oldDate = simpleOldDateFormat.parse(value)

return simpleNewDateFormat.format(oldDate)

Аннотация @DataSetDef

Аннотация @DataSetDef позволяет определять наборы данных при создании отчета во время разработки. Она имеет следующие атрибуты:

  • name - имя набора данных. Используется в наборах данных типа DELEGATE для связи определения набора данных с методом, возвращающим делегат.

    В кросс-табличном бэнде (@BandDef.orientation = Orientation.CROSS) наборы данных должны иметь следующие имена:

    • Заголовки столбцов: ${band_name}_dynamic_header

    • Заголовки строк: ${band_name}_master_data

    • Основное содержимое: ${band_name}

  • type - тип набора данных, определяемый перечислением DataSetType. Значения этого перечисления соответствуют типам наборов данных во время выполнения, описанным выше.

    Обычно в отчетах, создаваемых во время разработки, используются наборы данных типа DELEGATE. В этом случае они связываются по имени с методами, аннотированными аннотацией @DataSetDelegate.

Аннотация @DataSetDelegate

Аннотация @DataSetDelegate позволяет определить метод, который возвращает делегат для выполнения логики набора данных. Она имеет атрибут name, который должен быть равен имени соответствующего набора данных.

Аннотированный метод не должен иметь параметров и должен возвращать функциональный интерфейс ReportDataLoader.