JPQL 扩展
本章节介绍 Jmix 应用程序中可以使用的 JPQL(Java Persistence Query Language) 扩展。
会话和用户属性
JPQL 查询中可以使用通过 SessionData 创建的 会话属性,通过 session_ 前缀访问。例如,用 SessionData 设置一个属性:
@Autowired
private ObjectProvider<SessionData> sessionDataProvider;
void setCustomerCodeInSession(String code) {
    sessionDataProvider.getObject().setAttribute("customerCode", code);
}
然后在一个查询语句中这样使用 customerCode 属性:
select e from Customer e where e.code = :session_customerCode
与会话属性类似,可以用 current_user_ 前缀的参数访问当前认证用户的属性。例如,如果 User 实体 有 email 属性,在查询语句中可以这样使用(假设 Customer.manager 属性是用户实体的引用):
select e from Customer e where e.manager.email = :current_user_email
无需为 session_customerCode 或 current_user_email 参数设置值,框架会在执行查询前自动为其赋值。因此,在框架中某些无法完全控制查询语句执行的地方这种机制会很有用,例如,行级角色的 JPQL 策略 或 genericFilter 通用过滤器 的 JPQL 条件。
与 current_user_ 前缀的参数类似,还可以使用预定义的 current_locale 参数。
例如,JPQL 可以写成下面这样:
select e from Region e where e.locale = :current_locale
参数值从当前用户的会话中获取,会话通过 CurrentAuthentication 获取。
不区分大小写的子串搜索
可以在查询参数的值中使用 (?i) 前缀,即可针对字符串使用大小写不敏感的搜索条件。例如,下列查询:
select c from Customer c where c.name like :name
如果给 name 参数设定值为 (?i)%doe%,则如果数据库存在 John Doe 值,尽管大小写不一样也能匹配上。因为框架在运行查询语句时,会将参数条件改为 lower(C.NAME) like ?,并使用参数值 %doe% 进行查询。
注意,这种类型的查询不会使用 name 字段上的索引,不论是否存在这样的索引。
函数
下面表格列举出 JPQL 函数以及在 Jmix 中的支持程度。
| 函数 | 支持 | 查询语句 | 
|---|---|---|
聚合函数  | 
支持  | 
  | 
不支持:带纯数值计算的聚合函数(EclipseLink 功能)  | 
 
  | 
|
ALL、ANY、SOME  | 
支持  | 
  | 
计算函数(INDEX、SIZE、ABS、SQRT、MOD)  | 
支持  | 
 
 
 
 
  | 
CASE 表达式  | 
支持  | 
  | 
不支持:UPDATE 语句中的 CASE  | 
  | 
|
日期函数(CURRENT_DATE、CURRENT_TIME、CURRENT_TIMESTAMP)  | 
支持  | 
  | 
EclipseLink 函数(CAST、REGEXP、EXTRACT)  | 
支持  | 
 
 
  | 
不支持:GROUP BY 语句中的 CAST  | 
  | 
|
实体类型表达式  | 
支持:实体类型作为参数传递  | 
  | 
不支持:实体类型的直接关联  | 
  | 
|
函数调用  | 
支持:函数结果用于比较语句  | 
  | 
不支持:直接使用函数结果  | 
  | 
|
IN  | 
支持  | 
  | 
IS EMPTY 空集合  | 
支持  | 
  | 
KEY/VALUE  | 
不支持  | 
  | 
字面量  | 
支持  | 
 
 
 
 
 
  | 
不支持:日期和时间字面量  | 
 
 
  | 
|
MEMBER OF  | 
支持:字段或查询结果  | 
  | 
不支持:字面量  | 
  | 
|
NEW in SELECT  | 
支持  | 
  | 
NULLIF/COALESCE  | 
支持  | 
 
  | 
order by 中的 NULLS FIRST、NULLS LAST  | 
支持  | 
  | 
字符串函数(CONCAT、SUBSTRING、TRIM、LOWER、UPPER、LENGTH、LOCATE)  | 
支持  | 
 
 
 
 
 
 
  | 
不支持:使用特定字符的 TRIM  | 
  | 
|
子查询  | 
支持  | 
  | 
不支持:子查询的 FROM 中使用路径表达式而非实体名  | 
  | 
|
TREAT  | 
支持  | 
  | 
不支持:WHERE 语句中的 TREAT  | 
  | 
宏
JPQL 查询语句文本可以包含宏,宏会在语句执行前进行处理,转换成可执行的 JPQL,可以对查询参数做额外修改。
宏用于解决下列问题:
- 
当给定字段与当前时间有依赖时,JPQL 是无法表示的(即,不支持类似 "current_date - 1" 的表达式)。这里提供一种解决办法。
 - 
支持
Timestamp类型字段(date/time)与日期进行比较。 
@between
格式为 @between(field_name, moment1, moment2, time_unit) 或 @between(field_name, moment1, moment2, time_unit, user_timezone),其中:
- 
field_name是用于比较的属性名称。 - 
moment1、moment2- 检查field_name所属时间段的开始和结束点。开始和结束时间点都需要使用包含now变量的表达式定义,表达式可以对当前时间加减特定的整数值。 - 
time_unit- 定义表达式中从now加减整数值的时间单位和截取精度。可能值:year、month、day、hour、minute、second。 - 
user_timezone- 可选参数,定义当前用户的时区。 
这个宏会转换成 JQPL 表达式:field_name >= :moment1 and field_name < :moment2
示例 1:Customer 是今天创建的:
select c from Customer where @between(c.createTs, now, now+1, day)
示例 2:Customer 是前 10 分钟内创建的:
select c from Customer where @between(c.createTs, now-10, now, minute)
示例 3:过去 5 天内创建的文档,且考虑当前用户的时区:
select d from Doc where @between(d.createTs, now-5, now, day, user_timezone)
@today
格式为 @today(field_name) 或 @today(field_name, user_timezone),用于定义检查属性值是今天内的条件。本质上,这是 @between 宏的特殊情况。
示例:Customer 是今天创建的:
select d from Doc where @today(d.createTs)
@dateEquals
格式为 @dateEquals(field_name, parameter) 或 @dateEquals(field_name, parameter, user_timezone),用于定义检查 field_name 的值(Timestamp 格式)的日期与传入的参数 parameter 相等。
示例:
select d from Doc where @dateEquals(d.createTs, :param)
可以用 now 参数传入当前日期。如需设置日期偏移量,使用 + 或 - 运算符,示例:
select d from sales_Doc where @dateEquals(d.createTs, now-1)
@dateBefore
格式为 @dateBefore(field_name, parameter) 或 @dateBefore(field_name, parameter, user_timezone),用于定义检查 field_name 的值(Timestamp 格式)比传入的参数 parameter 早。
示例:
select d from Doc where @dateBefore(d.createTs, :param, user_timezone)
可以用 now 参数传入当前日期。如需设置日期偏移量,使用 + 或 - 运算符,示例:
select d from sales_Doc where @dateBefore(d.createTs, now+1)
@dateAfter
格式为 @dateAfter(field_name, parameter) 或 @dateAfter(field_name, parameter, user_timezone),用于定义检查 field_name 的值(Timestamp 格式)晚于或等于传入的参数 parameter。
示例:
select d from Doc where @dateAfter(d.createTs, :param)
可以用 now 参数传入当前日期。如需设置日期偏移量,使用 + 或 - 运算符,示例:
select d from Doc where @dateAfter(d.createTs, now-1)