JPA: кэширование запросов

Я использую JPA для загрузки и сохранения сущностей в моем веб-приложении на основе Java EE. Hibernate используется как реализация JPA, но я не использую специфичные для Hibernate функции и работаю только с чистым JPA.

Вот класс DAO, обратите внимание на метод getOrders :

class OrderDao {
  EntityManager em;

  List getOrders(Long customerId) {
    Query q = em.createQuery(
      "SELECT o FROM Order o WHERE o.customerId = :customerId");
    q.setParameter("customerId", customerId);
    return q.getResultList();
  }
}

Метод довольно прост, но у него есть большой недостаток. Каждый раз, когда вызывается метод, где-то в рамках реализации JPA выполняются следующие действия:

  1. Выражение JPQL анализируется и компилируется в SQL.
  2. Создается и инициализируется экземпляр оператора или PreparedStatement.
  3. Экземпляр оператора заполняется параметрами и выполняется.

Я считаю, что шаги 1 и 2 выше должны выполняться один раз за время существования приложения. Но как это сделать? Другими словами, мне нужно, чтобы эти экземпляры Query были кэшированы.

Конечно, я могу реализовать такой кеш на своей стороне. Но подождите, я использую современные мощные ORM! Разве они уже не сделали это для меня?

Обратите внимание, что я не упоминаю что-то вроде кеша запросов Hibernate, который кэширует результаты запросов. Здесь я хотел бы выполнить свои запросы немного быстрее.

15
задан Arjan Tijms 25 July 2013 в 06:55
поделиться

4 ответа

Кэш планов запросов в Hibernate. Таким образом, HQL не анализируется каждый раз, когда вызывается DAO (поэтому № 1 действительно встречается только один раз за время жизни вашего приложения). Это QueryPlanCache. Это не сильно документировано, так как оно «просто работает». Но вы можете найти больше информации здесь.

6
ответ дан 1 December 2019 в 01:52
поделиться

NamedQueries — это концепция, которую вы ищете.

4
ответ дан 1 December 2019 в 01:52
поделиться

Вам нужен NamedQuery. В объекте Order вы указываете:

@NamedQueries({
    @NamedQuery( name = "getOrderByCustomerId", query = "SELECT o FROM Order o WHERE o.customerId = :customerId")
})

Затем в DAO используйте em.createNamedQuery("getOrderByCustomerId") вместо повторного создания запроса.

2
ответ дан 1 December 2019 в 01:52
поделиться

Использовать статически определенные именованные запросы. Они более эффективны, поскольку поставщик сохраняемости JPA может преобразовать строку JP QL в SQL один раз во время запуска приложения, а не каждый раз при выполнении запроса, и рекомендуется, в частности, для часто выполняемых запросов.

Именованный запрос определяется с помощью аннотации @NamedQuery, которая обычно используется в классе сущностей результата. В вашем случае для сущности Order:

@Entity
@NamedQueries({
    @NamedQuery(name="Order.findAll",
                query="SELECT o FROM Order o"),
    @NamedQuery(name="Order.findByPrimaryKey",
                query="SELECT o FROM Order o WHERE o.id = :id"),
    @NamedQuery(name="Order.findByCustomerId",
                query="SELECT o FROM Order o WHERE o.customerId = :customerId")
})
public class Order implements Serializable {
    ...
}

Также рекомендуется ставить именованные запросы с префиксом имени сущности (чтобы иметь какое-то пространство имен и избежать коллизий).

А затем в DAO:

class OrderDao {
    EntityManager em;

    List getOrders(Long customerId) {
        return em.createNamedQuery("Order.findByCustomerId")
                 .setParameter("customerId", customerId);
                 .getResultList();
    }
}

PS: Я повторно использовал запрос, который вы предложили в качестве примера, но как-то странно иметь customerId в Заказе, я ожидаю, что Клиент.

Ссылки

  • Спецификация JPA 1.0
    • Раздел 3.6.4 «Именованные запросы»
18
ответ дан 1 December 2019 в 01:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: