说说 Jaspersoft Studio 中的参数以及与应用之间的交互
标签: 企业报表设计指南(Jaspersoft Studio+jasperreport)
报表中的参数(Parameters )是应用程序与报表引擎之间沟通的桥梁,它可以用于以下用途:
传递 SQL 查询中的参数值,比如 SQL 语句中的 where 条件。
提供数据源所无法提供的值,比如自定义表头、生成该报表的账号、应用所指定的图片路径等等。
参数由一个名称和一个 Java 实现类(Class)组成。比如 java.sql.Connection 类型的参数,一般用于子报表(subreport);而 java.lang.Boolean 类型的参数,用于是否展示报表的某一部分。
参数可以设定一个默认值,这样如果应用没有传递该参数值,JasperReports 也能正确解析。
1 管理参数
可以使用 outline 面板来管理参数。
Parameters 下的那些灰色参数,表示是系统级的参数,不能编辑或者删除它。
右键点击 Parameters ,就可以创建参数:
右键点击某个具体参数,就可以选择删除它。
点击某个具体参数,就可以在 Properties 面板中,对其进行查看或设置:
有以下这些可设置项:
设置项 | 说明 |
---|---|
Name | 参数名。 |
Class | 参数实现类。 |
Default Value Expression | 参数默认值,它可以是一个表达式。 |
Description | 参数说明。JasperReports 没有直接使用该值,但为了便于未来维护,建议还是要填写下。 |
Is for Prompting | 如果勾选该项,当在 Jaspersoft Studio 中点击 Preview 预览报表时,就会弹出参数输入框,供预览者依据实际情况填写报表所需的参数值。 |
在 Properties 面板的 Advanced 选项卡中,可以按照键值对的方式,对其进行设置:
2 内置参数
Jaspersoft Studio 定义了一些内置参数,具体说明如下:
内置参数 | 说明 |
---|---|
REPORT_PARAMETERS_MAP | 使用者调用 API 中的 fillReport() 方法所传入的 Map 型键值对参数。 |
REPORT_CONNECTION | 报表中的 JDBC connection,用于执行 SQL 语句。 |
REPORT_MAX_COUNT | 报表所能填充的最大记录数。如果未指定,则没有限制。 |
REPORT_DATA_SOURCE | 如果没有使用 JDBC connection(比如非数据库的数据源 CSV 等),将会使用这个参数。 |
REPORT_SCRIPTLET | 报表创建时的脚本实例,默认使用 net.sf.jasperreports.engine.JRDefaultScriptlet。 |
REPORT_LOCALE | locale 区域设置,一般用于国际化场景。默认为系统值。 |
REPORT_TIME_ZONE | 所在时区,默认为系统值。 |
REPORT_FORMAT_FACTORY | 默认实现是 net.sf.jasperreports.engine.util.FormatFactory。可以参考该工厂类,实现自定义工厂。 |
REPORT_CLASS_LOADER | 可用于设置报表填充时的 class loader。 |
REPORT_URL_HANDLER_FACTORY | 可用于指定创建 URL handlers 实现工厂。 |
REPORT_FILE_RESOLVER | 默认实现是 net.sf.jasperreports.engine.util.FileResolver,用于解析报表中的资源路径,也可以自定义。 |
REPORT_VIRTUALIZER | 定义报表填充器的实现类,默认为 JRVirtualizer 接口的实现类。 |
IS_IGNORE_PAGINATION | Boolean 类型,分页开关。默认情况下,除了导出 excel 与 HTML 之外,其它情况不分页。 |
3 查询场景
一般情况下,JasperReports 会把传入的参数放入 sql statement 中,然后再执行查询,这样可以避免 SQL 注入问题。当然这也会使得参数不够灵活,无法把 SQL 片段作为参数传入语句。
3.1 SQL 语句中的参数
参数可以定义在 where 条件中,用于过滤数据,语法为 $P{参数名},比如:
1 | ... and T3.STAFF_NO = $P{staff_no} |
这条语句将会以 prepared statement 形式加以执行:
1 | ... and T3.STAFF_NO = ? |
在 Jaspersoft Studio 的 Input Parameters 中,输入 staff_no 参数(记得先定义好这个参数)值:
输出结果:
参数也可以传入 SQL 片段,用于拼接 SQL 语句,语法为 $P!{参数名},比如:
1 | and $P!{conditions} |
在 Jaspersoft Studio 的 Input Parameters 中,输入 conditions 参数值:``T3.STAFF_NO = 1`,输出结果与上例相同。
利用该语法,甚至可以把整个 SQL 语句作为参数传入报表引擎。形如 $P!{自定义SQL语句}
一个 query 可以包含任意个入参,当遇到语法 $P!{xxx} 时,就会被替换为所传入的实际值。
注意: 必须为参数设置默认值,让报表更健壮。
3.2 处理 SQL 语句中的 NULL
$P{parametername} 格式的参数,无法正确处理 Null 值,我们可以使用 $X{EQUAL,fieldname,parametername}来处理 Null 值。
比较这两种写法的区别:
$P{param} 模式的语句为
1 | select * from client where address = $P{address_param} |
$X{EQUAL, column_name, param_name} 模式的语句为
1 | select * from client where $X{EQUAL, address , address_param} |
如果 address_param 参数值不为 Null,那么这两种写法,都会被解析为
1 | select * from client where address = '北京'。 |
如果 address_param 参数值为 Null,那么 $P{} 模式会被解析为
1 | select * from client where address = null,而 |
$X{} 模式会被解析为
1 | select * from client where address is null。 |
很明显,$X{} 的解析模式才是正确的 SQL 处理 null 的模式。
3.3 处理 SQL 语句中的 IN 与 NOT IN 形式
JasperReports 使用特殊语法来应对 where 中的 IN 与 NOT IN 形式。
IN 一般用于一系列的值,比如:
1 | select * from client where address in ('北京','上海','广州') |
address 的参数值一般是一个列表(最好是 java.util.Collection)或者一个数组,之前的 SQL 可以用以下语法来表示:
1 | select * from client where $X{IN, address,address_values} |
address_values 是包含一系列地址数据的参数,$X{} 支持三个入参:
IN 或者 NOTIN
字段名
参数名
如果传入的参数是 null 或者是一个空列表,$X{} 会解析为 0 = 0。
3.4 处理时间
3.4.1 表达式
可以使用类型为 DateRange 的参数,来作为时间条件,过滤数据。
表达式格式为 <Keyword>+/-<N>,具体说明如下:
<Keyword> 指定时间单位,可为 DAY, WEEK, MONTH, QUARTER, SEMI 和 YEAR。
<+/-> 表示是在该时间之前(-)还是之后(+)。
<N> 表示 N 个时间单位。
比如 DAY - 1 表示前一天。
这种表达方式存在一定的局限性,只能表示某个时间点的左右偏移量。如果要表示时间范围,可以用 BETWEEN 语法:
1 | $X{BETWEEN, column, startParam, endParam} |
比如说要设置当前周到某一天的,可以这样表示:
1 | $X{BETWEEN, column, WEEK, DAY} |
3.4.2 时间范围参数类型
参数类型 | 说明 | 示例 |
---|---|---|
net.sf.jasperreports.types.date.DateRange | 以 YYYY-MM-DD 格式表示的日期字符串。 | <parameter name=”myParam” class=”net.sf.jasperreports.types.date.DateRange”> |
net.sf.jasperreports.types.date.TimestampRange | 以 YYYY-MM-DD HH:mm:ss 格式表示的日期时间字符串。 | <parameter name=”myParam” class=”net.sf.jasperreports.types.date.TimestampRange”> |
3.4.3 在查询中的写法
必须使用 $X{} 表达式来处理时间范围。
可以使用 DateRangeBuilder() 类把时间范围参数的默认表达式转换为相应的时间范围参数类型。
示例 | 说明 |
---|---|
new net.sf.jasperreports.types.date.DateRangeBuilder(“DAY-1”).toDateRange() | 表示前一天,并转换为 DateRange 类型。 |
new net.sf.jasperreports.types.date.DateRangeBuilder(“WEEK”).set(Timestamp.class).toDateRange() | 表示一整周,并转换为 TimestampRange 类型。 |
new net.sf.jasperreports.types.date.DateRangeBuilder(“2020-02-01”).toDateRange() | 把日期字符串,转换为 DateRange 类型。 |
new net.sf.jasperreports.types.date.DateRangeBuilder(“2020-02-01 12:33:55”).toDateRange() | 把时间字符串,转换为 TimestampRange 类型。 |
比如说,需要展示前一天的数据,可以这样定义参数与 SQL 表达式(JRXML 形式):
<parameter name="myParameter" class="net.sf.jasperreports.types.date.DateRange">
<defaultValueExpression>
<![CDATA[new DateRangeBuilder("DAY-1").toDateRange()]]>
</defaultValueExpression>
</parameter>
<queryString>
<![CDATA[Select * from account where $X{EQUAL, create_date, myParameter}]]>
</queryString>
展示上个月最后一天之前的数据:
<parameter class="net.sf.jasperreports.types.date.DateRange" name="EndDate">
<defaultValueExpression>
<![CDATA[new net.sf.jasperreports.types.date.DateRangeBuilder("MONTH-1").toDateRange().getEnd()]]>
</defaultValueExpression>
</parameter>
<queryString>
<![CDATA[SELECT * FROM orders WHERE $X{LESS, order_date, EndDate}]]>
</queryString>
指定时间范围或者相对入参的时间偏移量,可以照以下步骤进行:
定义两个时间范围入参并指定好类型,比如 StartDate 与 EndDate。
设置该参数的默认表达式,此为可选项。
在查询语句中使用带 BETWEEN 函数的
$X{} 表达式。
比如希望查找 1 年前的所有数据,StartDate 表示 1 年前,EndDate 表示精确到日,可以这样表示:
<parameter name="StartDate" class="net.sf.jasperreports.types.date.DateRange">
<defaultValueExpression>
<![CDATA[(new net.sf.jasperreports.types.date.DateRangeBuilder("YEAR-1")).toDateRange()]]>
</defaultValueExpression>
</parameter>
<parameter name="EndDate" class="net.sf.jasperreports.types.date.DateRange">
<defaultValueExpression>
<![CDATA[(new net.sf.jasperreports.types.date.DateRangeBuilder("DAY")).toDateRange()]]>
</defaultValueExpression>
</parameter>
<queryString language="SQL">
<![CDATA[select name from employee where$X{BETWEEN, HIRE_DATE, StartDate, EndDate} limit 200]]>
</queryString>
还可以使用 getStart() 与 getEnd() 方法来精确指定时间范围的起止点,比如入参 Period 是时间范围,而我们实际需要的是 Date,那么可以这样表示:
<parameter name="StartDate" class="java.util.Date" nestedType="java.util.Date">
<defaultValueExpression>
<![CDATA[$P{Period}.getStart()]]>
</defaultValueExpression>
</parameter>
3.5 代码中传参
使用 HashMap 来传递参数,代码模板如下:
...HashMap hm = new HashMap();...JasperPrint print = JasperFillManager.fillReport(fileName,hm,new JREmptyDataSource());...
HashMap 是键值对结构,设置参数值是可以这样写:
...HashMap hm = new HashMap();hm.put("参数名","参数值")...
如果设置的参数值为 null,那么 JasperReports 会使用默认值表达式赋予初始值。
4 测试
可以在 Jaspersoft Studio 中把参数设置为 Prompt,这样可以在 Preview 预览模式中,填入报表入参,验证生成的报表数据是否满足业务需求。
在左侧 Parameters 中,点击某个需要设置 Prompt 的参数,然后在对应的 Properties 中,把 Is For Prompting 设置为 true:
接着点击 Preview 进入预览模式,就会出现 Input Parameters,用于输入需要测试的报表入参。输入入参后,点击下面的执行按钮,就可以测试我们所设计的报表咯:
Jaspersoft Studio 对入参考虑的比较全面,关键就是多实践。选择最适合的方式,来实现业务所需的报表。