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 条件。
不区分大小写的子串搜索
可以在查询参数的值中使用 (?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)