Я использую 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 и 2 выше должны выполняться один раз за время существования приложения. Но как это сделать? Другими словами, мне нужно, чтобы эти экземпляры Query были кэшированы.
Конечно, я могу реализовать такой кеш на своей стороне. Но подождите, я использую современные мощные ORM! Разве они уже не сделали это для меня?
Обратите внимание, что я не упоминаю что-то вроде кеша запросов Hibernate, который кэширует результаты запросов. Здесь я хотел бы выполнить свои запросы немного быстрее.
Кэш планов запросов в Hibernate. Таким образом, HQL не анализируется каждый раз, когда вызывается DAO (поэтому № 1 действительно встречается только один раз за время жизни вашего приложения). Это QueryPlanCache. Это не сильно документировано, так как оно «просто работает». Но вы можете найти больше информации здесь.
Вам нужен NamedQuery. В объекте Order вы указываете:
@NamedQueries({
@NamedQuery( name = "getOrderByCustomerId", query = "SELECT o FROM Order o WHERE o.customerId = :customerId")
})
Затем в DAO используйте em.createNamedQuery("getOrderByCustomerId") вместо повторного создания запроса.
Использовать статически определенные именованные запросы. Они более эффективны, поскольку поставщик сохраняемости 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
в Заказе
, я ожидаю, что Клиент
.