报表数据结构

定义数据结构

创建和编辑报表时,默认展示报表的 Report structure(报表结构) 标签页:

report structure
Figure 1. 报表数据结构
通用报表属性

界面顶部展示通用报表属性:

  • Name(名称) - 报表名称。该名称可以在 Localization(本地化) 标签页中本地化。

  • Group(报表组) - 报表组,用于在标准报表浏览界面中进行分组。

  • Default template(默认模板) - 报表输出 模板

  • System code(系统编码) - 可选,可用于在应用程序代码中标识报表。

报表带区定义

报表数据结构的主要元素是数据区(带区)结构 - Report bands(报表数据区)

一个报表带区可以有下列参数:

  • Band name(数据区名称) - 报表中的唯一带区名称。必须只包含拉丁字母、数字和下划线。此外,如果带区名称以 header 开头,则其数据不会在 表格 中输出。

  • Orientation(方向) - 报表带区方向:Horizontal(水平)Vertical(垂直)Crosstab(双向)

    • Horizontal(水平) - 报表带区向下复制,可以包括子带区;

    • Vertical(垂直) - 报表带区向右复制;

    • Crosstab(双向) - 报表带区向右和向下复制为矩阵。

  • Parent band(父数据区) - 父带区。

每个报表带区包括一个或多个数据集。在运行报表时,数据集将转换为行的列表,其中每行都是包含 name-value 对的映射。报表中出现的带区的次数与其最长数据集中的行数一样多。

字段名称可以用于报表模板中,并在生成报表时替换为数据集中的相应值。在描述数据集时,可以使用报表的 外部参数 以及其他报表带区的字段,这样可以创建关联带区。

每个报表都有 Root 带区。可以在其中创建数据集并从其他带区引用这些数据集的字段,但不能在报表模板中使用 Root 带区。

Dataset name(数据集名称) 列的值仅为方便用户。

数据集类型

SQL

SQL 数据集是通过执行 SQL 查询而生成的。建议使用 as 运算符为查询结果字段使用别名。也建议将别名用双引号括起来,以防止 DBMS 进行可能的大小写转换:

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

可以在查询中使用报表输入参数和父带区字段。参数需要用 ${} 处理,例如 ${dateFrom}。父带区字段也可以类似地处理,通过在字段名称前面添加带区名称:${band1.field1}

下面是一个使用从 group 父带区获取的 groupId 参数和外部 enabled 参数的 SQL 查询示例:

select u.name as "userName", u.login as "userLogin"
from jmxrpr_user u
where u.group_id = ${group.groupId}
    and u.enabled = ${enabled}
    and u.delete_ts is null

SQL 查询中应手动包含条件以过滤掉软删除的记录。

选择数据存储

默认情况下,SQL 查询在主数据库上执行。如果要查询 附加数据存储,请在 Data store(数据存储) 字段中设置其名称。

报表带区中的查询预处理

如果需要根据报表输入参数或父带区中的参数值动态修改 SQL/JPQL 查询,则可以使用 SQL 预处理。模板引擎支持使用 Groovy 修改 SQL/JPQ 查询语句。如需使用,请在报表带区编辑界面下方选中 Preprocess query as Groovy template(将查询语句预处理为 Groovy 模板) 复选框。生成的查询将由 GStringTemplateEngine 处理,该引擎可以访问:

  • 报表参数:${<parameter_name>}

  • 父带区中的值:${<band_name>.<parameter_name>}

考虑以下示例:根据是否传递 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 运算符提供别名。可以在 JPQL 查询中使用报表输入参数和父带区字段,类似 SQL 查询。

下面是一个使用从 group 父带区获取的 groupId 参数和外部 enabled 参数的 JPQL 查询示例:

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

JPQL 查询自动支持软删除并仅返回未删除的记录。

也可以使用 Link field(链接字段) 合并多个数据集,使用 Data store(数据存储) 设置 查询附加数据存储,或者勾选 Preprocess query as Groovy template(将查询语句预处理为 Groovy 模板) 启用 查询语句预处理

Groovy

Groovy 数据集是通过执行 Groovy 脚本而生成的。该脚本返回 List<Map<String, Object>> 类型的对象。此对象列表的每个元素都是 Map<String, Object> 类型的对象 - 对应于一个数据集记录。

脚本中可以访问下列对象:

  • params - 外部报表参数映射。以下是获取参数值的示例:

    def active = params['active']
  • parentBand - 父带区对象,类型为 com.haulmont.yarg.structure.BandData。此对象支持通过调用 getParameterValue() 方法获取父带区字段值,例如:getParameterValue() method,示例:

    def groupId = parentBand.getParameterValue('groupId')
  • currentAuthentication - io.jmix.core.security.CurrentAuthentication 类型的对象,关联当前的认证用户。示例:

    def user = currentAuthentication.getUser()
  • metadata - io.jmix.core.Metadata 类型的对象,提供对应用程序元数据的访问。示例:

    def metaClass = metadata.getClass('jmxrpr_User')
  • dataManager - io.jmix.core.DataManager 类型的对象,提供 CRUD 功能。示例:

    def book = dataManager.load(Book).id(bookId).fetchPlan("book.edit").one()
  • timeSource - io.jmix.core.TimeSource 类型的对象,用于获取当前时间。示例:

    def currentDate = timeSource.currentTimestamp()
  • applicationContext - org.springframework.context.ApplicationContext 类型的对象,提供访问托管 bean 的功能。示例:

    def accountService = applicationContext.getBean(AccountService);

实体

Entity(单一实体) 数据集仅包含一行数据,使用单个 JPA 实体实例的属性和与之相关的实体生成。

数据源由 Entity(单一实体) 类型的外部参数生成,必须在 Parameters and Formats(参数和格式) 标签页中进行描述。Entity parameter name(实体参数名称) 字段中的值必须与参数名称匹配。

报表模板必须包含具有实体属性名称的字段。属性名称可以使用已有 fetch plan 中的名称,需要勾选 Use existing fetch plan(使用已有 fetch plan),或者用 Select entity attributes(选择实体属性) 按钮选择。

由于报表引擎每次都会重新加载 Entity(单一实体) 数据集,因此不能用于不保存在数据库的 DTO 实体或 JPA 实体。此时,可以使用 Groovy 数据集,将实体作为参数传入:

def entity = params['entity']

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

实体列表

List of entities(实体列表) 数据集由一组 JPA 实体实例生成。

数据源由 List of entities(实体列表) 类型的外部参数生成,必须在 Parameters and Formats(参数和格式) 标签页中进行描述。Entity parameter name(实体参数名称) 字段中的值必须与参数名称匹配。

报表模板必须包含具有实体属性名称的字段。属性名称可以使用已有 fetch plan 中的名称,需要勾选 Use existing fetch plan(使用已有 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

    报表引擎将对 URL 执行 GET HTTP 查询。

    示例:

    https://jsonplaceholder.typicode.com/users
  3. 字符串类型的参数

    必须在 Parameters and Formats(参数和格式) 标签页中描述包含 JSON 数据的 String 类型的报表外部参数。

使用 JsonPath 查询获取 JSON 树。例如,可以使用 $.store.book[*] JsonPath 返回以下 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 表达式的更多详细信息,请参阅 文档

报表输出的字段如果是 DateDateTimeTime 数据类型,则不支持 java.text.SimpleDateFormat 定义的格式。如果要设置正确的格式,需要写一个 Groovy 脚本。

如需使用这个格式,可以切换到报表编辑器的 Parameters and Formats(参数和格式) 标签页并打开 formatter 编辑器。例如,对于 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)