3.4.4.4. Выполнение JPQL запросов

В данном разделе описывается интерфейс Query, предназначенный для выполнения JPQL запросов на уровне ORM. Ссылку на Query можно получить у текущего экземпляра EntityManager вызовом метода createQuery(). Если запрос предполагается использовать для извлечения сущностей, рекомендуется вызывать createQuery() с передачей типа результата, что приведет к созданию TypedQuery.

Методы Query в основном соответствуют методам стандартного интерфейса JPA javax.persistence.Query. Рассмотрим отличия.

  • setView(), addView() - устанавливают представление, используемое при загрузке данных.

  • getDelegate() - возвращает экземпляр javax.persistence.Query, предоставляемый реализацией ORM.

Если для Query установлено представление, то по умолчанию Query имеет FlushModeType.AUTO, что влияет на случай, когда в текущем персистентном контексте содержатся измененные экземпляры сущностей: эти экземпляры будут сохранены в БД перед выполнением запроса. Другими словами, ORM сначала синхронизирует состояние сущностей в персистентном контексте и в БД, а уже потом выполняет запрос. Этим гарантируется, что в результаты запроса попадут все соответствующие экземпляры, даже если они еще не были сохранены в базе данных явно. Обратной стороной этого является неявный flush, т.е. выполнение команд SQL update для всех измененных в данном контексте сущностей, что может повлиять на производительность.

Если же Query выполняется без представления, то по умолчанию Query имеет FlushModeType.COMMIT, что означает, что неявный flush вызван не будет, и запрос не будет учитывать содержимое текущего персистентного контекста.

В большинстве случаев игнорирование текущего персистентного контекста допустимо, и является предпочтительным поведением, так как не вызывает дополнительных команд SQL. Однако, при использовании представлений существует следующая проблема: если в персистентном контексте есть измененный экземпляр сущности, и выполняется запрос с представлением и FlushModeType.COMMIT, загружающий этот же экземпляр, то изменения будут потеряны. Поэтому по умолчанию мы используем FlushModeType.AUTO для запросов с представлением.

Вы также можете явно установить flush mode с помощью метода setFlushMode() интерфейса Query, чтобы переопределить режим по умолчанию.

Using DELETE FROM with soft-deleted entities

Если в проекте используется мягкое удаление, то при выполнении JPQL-запроса DELETE FROM для сущности, удалённой через мягкое удаление, будет выброшено исключение. Дело в том, что такой запрос, по сути, будет трансформирован в запрос SQL для удаления всех сущностей, не помеченных для удаления. По умолчанию такое неочевидное поведение запрещено, но его можно разрешить с помощью свойства приложения cuba.enableDeleteStatementInSoftDeleteMode.

Query Hints

Метод Query.setHint() позволяет добавить некоторые подсказки (hints) в генерируемые команды SQL. Подсказки обычно используются для указания того, как запрос должен использовать индексы, или другую специфику СУБД. Фреймворк определяет следующие константы, которые можно использовать в качестве имен задаваемых подсказок:

  • QueryHints.SQL_HINT - значение подсказки будет добавлено после сгенерированной команды SQL. Указывайте полный текст подсказки, включая разделители комментариев, если они требуются.

  • QueryHints.MSSQL_RECOMPILE_HINT - будет добавлено выражение OPTION(RECOMPILE) для СУБД MS SQL Server. Значение подсказки при этом игнорируется.

При работе с DataManager подсказки в запрос можно передать, используя метод LoadContext.setHint().