带区和数据集
报表的内部结构由 bands(带区) 组成。一个带区表示报表中的一个部分,该部分在最终文档中可能出现零次或多次。
带区中填充的数据由一个或多个与该带区关联的 datasets(数据集) 提供。
在报表模板中,带区的标记方式取决于文件格式。报表定义中包含带区的层次结构,与模板中的带区相对应。
下图展示了报表中使用的带区示例:
报表定义中的 Root 带区是预定义的,但模板中可能不会用到。
| 另请参阅 Report Generation 指南,其中包含具有复杂结构和各种输出类型的报表的实际示例。 |
带区
带区标签页
应用程序运行时,可以在报表编辑器中的 Bands 标签页定义带区结构和每个带区的数据集。
一个报表带区包含下列参数:
每个报表带区包括一个或多个数据集。在运行报表时,数据集将转换为行的列表,其中每行都是包含名值对(name-value)的映射。报表中出现的带区的次数与其最长数据集中的行数一样多。
报表模板中定义的字段在执行报表时会被数据集中的相应值替换。在定义数据集时,可以使用 报表外部参数 和其他带区的字段(用于关联带区)。
每个报表都有一个 Root 带区。可以在其中创建数据集,并从其他带区引用其字段。
请注意,Dataset name 列(在使用多个数据集时可用)仅供方便使用。
@BandDef 注解
在设计时可以用 @BandDef 定义带区。该注解有以下属性:
-
name— 带区名称,在报表内唯一。只能包含拉丁字母、数字和下划线。 -
parent— 父带区的名称。只有Root带区可以省略。 -
root— 对于Root带区必须设置为true。默认为false。 -
orientation— 带区的方向,由Orientation枚举定义:HORIZONTAL、VERTICAL、CROSS。默认为HORIZONTAL。 -
dataSets— 由一组@DataSetDef注解定义的数据集。
报表定义必须包含带有 name = "Root" 和 root = true 属性的 Root 带区。
报表定义 Java 类中 @BandDef 注解的顺序很重要。会影响同一层级带区的打印顺序。
|
数据集
SQL
SQL 数据集是通过执行 SQL 查询而生成的。建议使用 as 运算符为查询结果字段设置别名。最好将别名用双引号括起来,以防止 DBMS 进行可能的大小写转换:
select u.name as "userName", u.login as "userLogin"
from user_ u
可以在查询中使用报表输入参数和父带区字段。参数需要用 ${} 处理,例如 ${dateFrom}。父带区字段也可以类似地处理,通过在字段名称前面添加带区名称:${band1.field1}。
下面是一个使用从 group 父带区获取的 groupId 参数和外部 enabled 参数的 SQL 查询示例:
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 查询中需要指定条件过滤软删除的记录。 |
- 合并多个数据集
-
Link field(链接字段) 用于合并来自同一带区内的多个数据集的数据。当单个查询或 Groovy 脚本无法获取报表所需的完整数据时使用。
- 选择数据存储
-
默认情况下,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 User u
where u.group.id = ${group.groupId}
and u.active = ${active}
JPQL 查询自动支持软删除并仅返回未删除的记录。
|
请注意,当使用 JPQL 查询语句作为报表的数据源时,查询语句绕过 DataManager 直接在数据库执行。也就是说,不会使用 系统的 数据访问安全检查(行级别和资源级别)。因此,开发者必须确保 JPQL 查询仅检索用户被授权访问的数据。开发者有责任在查询中实施任何必要的安全检查。 |
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类型的对象,提供访问托管 bean 的功能。示例:def accountService = applicationContext.getBean(AccountService);
实体
Entity(单一实体) 数据集仅包含一行数据,使用单个 JPA 实体实例的属性和与之相关的实体生成。
数据源由 Entity 类型的外部参数生成,必须在 Parameters(参数) 标签页中进行描述。Entity parameter name(实体参数名称) 字段中的值必须与参数名称匹配。
报表模板必须包含具有实体属性名称的字段。属性名称可以使用已有 fetch plan 中的名称,需要勾选 Use existing fetch plan(使用已有 fetch plan),或者用 Select entity attributes(选择实体属性) 按钮选择。
|
由于报表引擎每次都会重新加载 Entity 数据集,因此不能用于未在数据库保存的 DTO 实体或 JPA 实体。此时,可以使用 Groovy 数据集,将实体作为参数传入:
|
实体列表
List of entities(实体列表) 数据集由一组 JPA 实体实例生成。
数据源由 List of entities 类型的外部参数生成,必须在 Parameters(参数) 标签页中进行描述。Entity parameter name(实体参数名称) 字段中的值必须与参数名称匹配。
报表模板必须包含具有实体属性名称的字段。属性名称可以使用已有 fetch plan 中的名称,需要勾选 Use existing fetch plan(使用已有 fetch plan),或者用 Select entity attributes(选择实体属性) 按钮选择。
|
由于报表引擎每次都会重新加载 List of entities 数据集,因此不能用于未在数据库保存的 DTO 实体或 JPA 实体。此时,可以使用 Groovy 数据集,将实体列表作为参数传入:
|
JSON
JSON 数据集是通过 JSON 数据生成的。可以从以下来源获取此数据:
-
Groovy 脚本
用户提供的脚本应该以字符串形式返回 JSON 数据。
示例:
return ''' { "items": [ { "name": "Java Concurrency in practice", "price": 15000 }, { "name": "Clear code", "price": 13000 }, { "name": "Scala in action", "price": 12000 } ] } ''' -
URL
报表引擎将对 URL 执行 GET HTTP 查询。
示例:
https://jsonplaceholder.typicode.com/users -
参数
必须在 Parameters(参数) 标签页中描述包含 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 表达式的更多详细信息,请参阅 文档。
|
报表输出的字段如果是 如需使用这个格式,可以切换到报表详情视图的 Value formats(值格式) 标签页并打开 formatter 编辑器。例如,对于
|
@DataSetDef 注解
在设计时,使用 @DataSetDef 注解定义报表的数据集。该注解有以下属性:
-
name— 数据集名称。在DELEGATE类型的数据集中,用于关联数据集定义和返回代理的方法。在交叉表带区(
@BandDef.orientation = Orientation.CROSS)中,数据集必须使用以下名称:-
列标题:
${band_name}_dynamic_header -
行标题:
${band_name}_master_data -
主要内容:
${band_name}
-
-
type— 数据集类型,由DataSetType枚举定义。此枚举的值与上述运行时数据集的类型相对应。通常,设计时的报表使用
DELEGATE类型的数据集。此时,通过名称与使用@DataSetDelegate注解的方法关联。