Расширения 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-условий genericFilter компонента фильтра.
Вы также можете использовать предопределенный параметр запроса current_locale так же, как и параметры с префиксом current_user_.
Например, вы можете написать JPQL-запрос следующим образом:
select e from Region e where e.locale = :current_localeЗначение этого параметра определяется локалью текущей пользовательской сессии, которая извлекается из объекта CurrentAuthentication.
| Атрибуты пользователя ( current_user_*) считываются при логине и хранятся в памяти. Следовательно, если соответствующие значения обновляются в базе данных, они не становятся сразу доступными для кода приложения. | 
Поиск подстрок без учета регистра
Можно использовать префикс (?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)