我正在使用JPA在基于Java EE的Web应用程序中加载和持久化实体。 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查询缓存那样缓存查询结果的东西。在这里,我想更快地执行我的查询。
答案 0 :(得分:18)
使用静态定义的命名查询。它们更有效,因为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
上使用Order
有点奇怪,我希望Customer
代替。{/ p>
答案 1 :(得分:5)
这是Hibernate中的查询计划缓存。因此,每次调用DAO时都不会解析HQL(因此#1在应用程序生命周期中只出现一次)。这是QueryPlanCache。它没有大量记录,因为它“正常”。但您可以找到更多信息here。
答案 2 :(得分:4)
NamedQueries是您正在寻找的概念。
答案 3 :(得分:2)
你想要的是一个NamedQuery。在您的订单实体上:
@NamedQueries({
@NamedQuery( name = "getOrderByCustomerId", query = "SELECT o FROM Order o WHERE o.customerId = :customerId")
})
然后在您的DAO中使用em.createNamedQuery(“getOrderByCustomerId”)而不是重新创建查询。
答案 4 :(得分:1)
您无法准备未命名的查询。这是您应该尝试在代码中使用命名查询而不是简单查询的主要原因。 此外,可以缓存命名查询,而java代码中的简单查询则不能。当然,这是一个可选功能,可以使用命名查询的提示启用。
答案 5 :(得分:0)
JPA 2.1,section" 3.1.1 EntityManager Interface":
Query,TypedQuery,StoredProcedureQuery,CriteriaBuilder, 从实体获取的Metamodel和EntityTransaction对象 经理在实体经理开放时有效。
从这句话中带回家的教训是,只要实体经理保持开放,就只能缓存入伍的查询类型 - 我们对容器管理的实体经理一无所知。
我想到了三种解决方案。 1)其他人指出的命名查询。 2)改为缓存CriteriaQuery
,希望提供者可以从中进行某种优化。 3)使用应用程序管理的实体管理器(保持打开状态)。
@Stateless
public class OrderRepository
{
@PersistenceUnit
EntityManagerFactory emf;
@PersistenceContext
EntityManager em;
private CriteriaQuery<Order> query;
private Parameter<Long> param;
@PostConstruct
private void constructQuery() {
CriteriaBuilder b = emf.getCriteriaBuilder();
query = b.createQuery(Order.class);
param = b.parameter(long.class);
...
}
public List<Order> findByCustomerKey(long key) {
return em.createQuery(query)
.setParameter(param, key)
.getResultList();
}
}
@Stateless
public class OrderRepository
{
@PersistenceUnit
EntityManagerFactory emf;
private EntityManager em;
private TypedQuery<Order> query;
@PostConstruct
private void initialize() {
em = emf.createEntityManager();
query = em.createQuery("SELECT o FROM Order o WHERE o.id = ?1", Order.class);
}
public List<Order> findByCustomerKey(long key) {
try {
return query.setParameter(1, key)
.getResultList();
}
finally {
em.clear(); // returned entities are detached
}
}
@PreDestroy
private void closeEntityManager() {
em.close();
}
}