Using Named Queries

Status: In Early Access
Sean Brydon

Problem Description

Java EE 5 applications create domain models using the Java Persistence APIs. When designing code that will access your entities, the code will need to create queries. Some of the queries are constructed dynamically, and other queries are static. These static queries can take parameters but the query stays the same. As your application gets larger, the code can have a lot of queries and this can get difficult to manage.

Solution

The Java Persistence APIs allow the creation of named queries for these predefined queries. The Query APIs allow the code to use these named queries. You can use a named query whenever you have a query that you run several times and just set parameters for. These are often called static queries. You can do this for all your pre-defined queries.

Using the Named queries is a good practice for the following reasons: 

Named queries are thread-safe as they are meant to be reused by many classes and instances. Note that the names of named queries are scoped and it is important to avoid name clashes. When annotations are used to define named queries, the scope of the name is visible within a persistent unit. You can use the deployment descriptor to make the names visible at application scope. When using a named query in code, we use a naming convention of prefixing the class name of the class where the named queries are defined in front of the query name.

Named queries can only be defined on certain classes. They must be defined and placed only on entities and mapped superclasses.

Note that the named queries can be used for native SQL queries as well as for queries expressed in the Java Persistence query language.

Refactoring  code to use a named query 

The concept of named queries is simple and useful. Even though the named queries can be used for native SQL queries as well, the example we will use is based on a query written using Java Persistence query language.

Code Before Code After using Named Queries
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;
  }


Note that this code could put the query into a string constant but still its not pre-compiled.

 Note the annotation is used on the class:

@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;
  }

As can be noted from the example above, it is relatively simple to refactor code to use a named query. 

One minor design choice for named queries is whether to use positional parameters or named parameters. Named parameters are preferred as it makes the code easier to understand and maintain. The example code above shows a named query using a named parameter ":cID" with query="SELECT i FROM Item i WHERE i.product.categoryID LIKE :cID" which is then set by  calling query.setParameter("cID",catID). Its clean and easy.

Using the NamedQueries Annotation

When you have more than one named query, you can use the NamedQueries annotation to create a list of all the named queries used in a class. Here is an example of how it is used. Note that the annotation is used on the class:
@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 {...

Then your code can just use the each named query in the usual way,

public class MyDataFacade ... {
...
Query query = em.createNamedQuery("
MyEntity.getItemsPerProductCategory");
Its fairly simple, though the syntax of listing the named queries is somewhat non-intuitive at first.

References

Here are some references to consider:

© Sun Microsystems 2006. All of the material in The Java BluePrints Solutions Catalog is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.