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

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

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

  • setParameter() - устанавливает значение параметра запроса. При передаче в данный метод экземпляра сущности выполняет неявное преобразование экземпляра в его идентификатор. Например:

    Customer customer = ...;
    TypedQuery<Order> query = entityManager.createQuery(
        "select o from sales$Order o where o.customer.id = ?1", Order.class);
    query.setParameter(1, customer);

    Обратите внимание на сравнение в запросе по идентификатору, но передачу в качестве параметра самого экземпляра сущности.

    Вариант метода с передачей implicitConversions = false не выполняет такого преобразования.

  • 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, чтобы переопределить режим по умолчанию.