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

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

Методы Query в основном соответствуют методам стандартного интерфейса 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() - аналогичны одноименным методам интерфейса EntityManager - устанавливают представление, используемое при загрузке данных текущим запросом, не влияя на представление всего EntityManager.

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

При выполнении запроса через Query изменения в текущем персистентном контексте не учитываются, т.е. запрос просто выполняется в БД. Если результатом выборки являются экземпляры, уже находящиеся в персистентном контексте, то в результате запроса окажутся именно они, а не прочитанные из БД. Ситуацию поясняет следующий фрагмент теста:

TypedQuery<User> query;
List<User> list;

query = em.createQuery("select u from sec$User u where u.name = ?1", User.class);
query.setParameter(1, "testUser");
list = query.getResultList();
assertEquals(1, list.size());
User user = list.get(0);

user.setName("newName");

query = em.createQuery("select u from sec$User u where u.name = ?1", User.class);
query.setParameter(1, "testUser");
list = query.getResultList();
assertEquals(1, list.size());
User user1 = list.get(0);

assertTrue(user1 == user);

Такое поведение определяется параметром openjpa.IgnoreChanges=true, заданным в файле persistence.xml базового проекта cuba. В прикладном проекте данный параметр можно изменить, указав его в собственном persistence.xml.

Запросы, модифицирующие данные (update, delete) приводят к сбросу (flush) в базу данных текущего персистентного контекста перед выполнением. Другими словами, ORM сначала синхронизирует состояние сущностей в персистентном контексте и в БД, а уже потом выполняет модифицирующий запрос. Рекомендуется выполнять такие запросы в неизмененном персистентном контексте, чтобы исключить неявные действия ORM, которые могут отрицательно сказаться на производительности.