使用命名查询

状态:在 Early Access 版本阶段
作者:Sean Brydon

问题描述

Java EE 5 应用程序使用 Java 持久性 API 来创建域模型。设计用于访问实体的代码时,该代码需要创建查询。一些查询是动态构造的;而其他查询是静态的。这些静态查询可以使用参数,但查询保持不变。随着应用程序变得越来越大,代码可能会包含很多查询,从而变得难以管理。

解决方案

通过使用 Java 持久性 API,可以为这些预定义的查询创建命名查询。代码可通过“查询 API”来使用这些命名查询。如果要将某个查询运行几次并仅为其设置参数,则可以使用命名查询。这些查询通常称为静态查询。您可以为所有预定义的查询执行此操作。

使用命名查询是一种很好的做法,原因如下:

命名查询是线程安全的,因为它们必须可供很多类和实例重复使用。请注意,应指定命名查询名称的范围,这对于避免名称冲突至关重要。使用标注来定义命名查询时,名称范围在持久性单元中是可见的。可以使用部署描述符使名称在应用程序范围内可见。在代码中使用命名查询时,我们使用在类的名称前面加前缀的命名约定;在这种命名约定中,将命名查询定义在查询名称的前部。

只能在某些特定的类上定义命名查询。您只能在实体和映射的超类上定义和放置命名查询。

请注意,命名查询可用于本地 SQL 查询以及使用 Java 持久性查询语言表示的查询。

重构代码以使用命名查询

命名查询的概念很简单,并且非常有用。尽管命名查询也可以用于本地 SQL 查询,但我们使用的示例基于使用 Java 持久性查询语言编写的查询。

之前的代码 使用命名查询后的代码
public class MyDataFacade ... {
  private EntityManager em;
  ...
  public List<Items> getItems(String catID){
    Query query =
    em.createQuery("SELECT i FROM Item i WHERE i.product.categoryID LIKE :cID");
   
query.setParameter ("cID",catID);

    List<Item> items = query.getResultList();
    return items;
  }


请注意,此代码可以将查询放在字符串常量中,但仍然没有对其进行预编译。

请注意,在类上使用了标注:

@NamedQuery(
  name="
MyEntity.getItemsPerProductCategory",
  query="SELECT i FROM Item i WHERE i.product.categoryID LIKE :cID"
)
@Entity
public class MyEntity { ...

Then another class uses the named queries...
public class MyDataFacade ... {
  private EntityManager em;
  ...
  public List<Items> getItems(String catID) {
    Query query =
 em.createNamedQuery("MyEntity.getItemsPerProductCategory");     query.setParameter("cID",catID);

    List<Item> items = query.getResultList();
    return items;
  }

正像从上面的示例中所看到的那样,使用命名查询的重构代码要相对简单一些。

一个较小的命名查询设计选择是,使用位置参数还是使用命名参数。命名参数是首选的,因为它可使代码更易于理解和维护。上面的示例代码说明了在 query="SELECT i FROM Item i WHERE i.product.categoryID LIKE :cID" 中使用命名参数 ":cID" 的命名查询,随后调用 query.setParameter("cID",catID) 对其进行了设置。这非常简单明了。

使用 NamedQueries 标注

如果包含多个命名查询,可使用 NamedQueries 标注来创建类中使用的所有命名查询的列表。下面是如何使用此标注的示例。请注意,在类上使用了该标注:
@NamedQueries(
{
@NamedQuery(
name="
MyEntity.getItemsPerProductCategory",
query="SELECT i FROM Item i WHERE i.productID.categoryID LIKE :cID"
),
@NamedQuery(
name="
MyEntity.getAllZipCityState",
query="SELECT z FROM ZipLocation z"
)
}
)
@Entity
public class MyEntity {...

然后,代码只需按通常方式使用每个命名查询即可,

public class MyDataFacade ... {
...
Query query = em.createNamedQuery("
MyEntity.getItemsPerProductCategory");
虽然看上去命名查询的语法不太直观,实际上它是相当简单的。

参考资料

以下是一些值得参考的资料:

© Sun Microsystems 2006。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。