Расширения JPQL
В этом разделе описываются расширения Java Persistence Query Language, которые можно использовать в приложениях Jmix.
Атрибуты сессии и пользователя
Атрибуты сессии, созданные с помощью SessionData
, доступны в любом запросе JPQL с префиксом 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
является ссылкой на сущность User):
select e from Customer e where e.manager.email = :current_user_email
Вам не нужно задавать значение для параметров session_customerCode
или current_user_email
, так как они будут назначены автоматически перед выполнением запроса. Это особенно полезно при использовании тех функций фреймворка, где у вас нет полного контроля над выполнением запроса, например, JPQL-политик ролей уровня строк или JPQL-условий обычного фильтра.
Поиск подстрок без учета регистра
Можно использовать префикс (?i)
в значении параметра запроса, чтобы легко указать условия для поиска без учета регистра по любой части строки. Например, рассмотрим запрос:
select c from Customer c where c.name like :name
Если передать строку (?i)%doe%
в качестве значения параметра name
, запрос вернет John Doe
, если такая запись существует в базе данных, даже если регистр символов отличается. Это произойдет потому, что фреймворк выполнит SQL-запрос с условием lower(C.NAME) like ?
и значением %doe%
параметра.
Обратите внимание, что такой поиск не будет использовать индекс для поля имени, даже если таковой существует в базе данных.
Функции
в таблице ниже перечислены функции JPQL и их уровень поддержки в Jmix.
Функция | Поддержка | Запрос |
---|---|---|
Агрегатные функции |
ДА |
|
НЕТ: агрегатные функции со скалярным выражением (особенность EclipseLink) |
|
|
ALL, ANY, SOME |
ДА |
|
Арифметические функции (INDEX, SIZE, ABS, SQRT, MOD) |
ДА |
|
Выражения CASE |
ДА |
|
НЕТ: CASE в UPDATE-запросе |
|
|
Функции даты (CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP) |
ДА |
|
Функции EclipseLink (CAST, REGEXP, EXTRACT) |
ДА |
|
НЕТ: CAST в запросе GROUP BY |
|
|
Операторы типов сущности |
ДА: тип сущности передается как параметр |
|
НЕТ: прямая ссылка на сущность |
|
|
Вызов функций |
ДА: результат с операторами сравнения |
|
НЕТ: прямое использование результата функции |
|
|
IN |
ДА |
|
IS EMPTY для коллекций |
ДА |
|
KEY/VALUE |
НЕТ |
|
Литералы |
ДА |
|
НЕТ: литералы даты и времени |
|
|
MEMBER OF |
ДА: поля или результаты запроса |
|
НЕТ: литералы |
|
|
NEW в SELECT |
ДА |
|
NULLIF/COALESCE |
ДА |
|
NULLS FIRST, NULLS LAST в order by |
ДА |
|
Строковые функции (CONCAT, SUBSTRING, TRIM, LOWER, UPPER, LENGTH, LOCATE) |
ДА |
|
НЕТ: TRIM не поддерживается с trim char |
|
|
Вложенные запросы |
ДА |
|
НЕТ: path-выражения вместо имени сущности в FROM подзапроса |
|
|
TREAT |
ДА |
|
НЕТ: TREAT в WHERE-выражениях |
|
Макросы
Текст запроса JPQL может содержать макросы, которые обрабатываются перед выполнением запроса. Они преобразуются в исполнимый JPQL и могут дополнительно изменять набор параметров запроса.
Макросы решают следующие проблемы:
-
Обеспечивают обходной путь для ограничения JPQL, которое делает невозможным выражение условия зависимости данного поля от текущего времени (т.е. выражения типа "current_date - 1" не работают).
-
Позволяют сравнивать поля типа
Timestamp
(поля даты/времени) с датой.
@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
- необязательный аргумент, который, если задан, определяет, что в запросе должен учитываться часовой пояс текущего пользователя.
Макрос преобразуется в следующее выражение в JPQL: 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
. Чтобы задать смещение в днях, используйте 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
. Чтобы задать смещение в днях, используйте 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
. Чтобы задать смещение в днях, используйте now
с +
или -
, например:
select d from Doc where @dateAfter(d.createTs, now-1)