Tuesday, September 14, 2010

Type-safe JPQL query results with JPA 2.0

If you use the Eclipse IDE or Rational Application Developer (RAD) for your Java development, you probably make an effort to keep an eye out for warnings flagged in your code and spend some amount of time cleaning them up. That is why they are there, right? To help make sure your code is top notch. Warnings are even more prevalent if you have a large amount of pre-Java 5.0 code and have moved to Java 5 or Java 6. If you had a fairly warning-free code base and moved to Java 5 or newer you've probably encountered a barrage of new warnings in your workspace. The introduction of Java generics in Java 5.0 is one of the major culprits for all these new warnings. The use of collection classes (Collection, Set, List, etc.) that you happily used in your code are now all warning flagged as needing to be parameterized/typed.

If you are a user of JPA, normal usage of some of the JPA APIs (primarly those from JPA 1.0) will add to your warning woes. For example, result list processing of JPQL queries returns an untyped result list that will get flagged as needing type information. Let's say you have some code that creates a JPQL query, executes it, and prints out the result.

Query q = em.createQuery("SELECT e FROM Employee e");
List employees = q.getResultList();
if (employees != null) {
  for (int i = 0; i < employees.size(); i++) {
    Employee emp = (Employee)employees.get(i);
    System.out.println("Employee id=" + emp.getId() + ", name=" + e.getName());

The Eclipse IDE will flag the 2nd line as a warning, telling you that you need to parameterize the List. A quick and dirty way to get rid of the warning is to wildcard the List definition, informing the compiler the list is untyped.

List<?> employees = q.getResultList(); // Good-bye, warning!

This makes a dent in your warning count, but doesn't really buy you much. You are left with potentially unsafe type casts and result processing is still a bit messy. This is where a new feature/interface of JPA 2.0, TypedQuery shines. The TypedQuery interface provides the ability to operate on query results and parameters in a more type-safe manner. The TypedQuery interface is actually geared more toward the new programmatic Criteria API, but provides benefits for JPQL queries as well. Here's an example of how one might write the original code using a TypedQuery.

TypedQuery<Employee> tq = em.createQuery("SELECT e FROM Employee e",     Employee.class);
for (Employee emp : tq.getResultList()) {
  System.out.println("Employee id=" + emp.getId() + ", name=" + e.getName());

The compile time warnings are gone, the casts are gone, and we are able to use the new iterator-based for loop to cleanly process the query result. Looks great, right? Well, there is a caveat. Currently, JPQL-based TypedQuery does not support the use of the JPA 2.0 Tuple to process multi-valued results (for example, returning multiple entity types in a single "row" of the result). However, the JPA 2.0 Criteria API does provide full Tuple support - along with all around true type-safety for query, query result, and query parameters. With any luck the ability to use Tuple with JPQL may make its way in a future version of the JPA specification. But, if your JPQL queries primarily return a single value type, the TypedResult support for JPQL currently provided in JPA 2.0 could go a long way toward writing cleaner code.