Skip to main content

Comment lines: Roland Barcia: How useful are annotated named queries in the Java Persistence API, really?

Roland Barcia, Certified IT Specialist, IBM
Roland Barcia is a Certified IT Specialist for IBM Software Services for WebSphere in the New York/New Jersey Metro area. He is a co-author of IBM WebSphere: Deployment and Advanced Configuration. For more information on Roland, visit his Web site.

Summary:  Annotations do make things easier for a developer, but simplicity comes with trade-offs. The Java™ Persistence API (JPA) makes use of annotations as a mechanism to map Java objects to the underlying database, but developers often use annotations even when it doesn't make sense. Find out some of the other ways to access data through JPA, and when and why these alternatives are the better options.

Date:  19 Apr 2006
Level:  Introductory
Activity:  1098 views

From the IBM WebSphere Developer Technical Journal.

An annotation on annotations

The Java Persistence API (JPA) defines several ways to access data: either through the entity manager, through JPA-QL, or through native queries. JPA makes use of annotations as a mechanism to map Java objects to the underlying database. You can also provide XML metadata as an overriding or alternative mechanism to the mapped annotations. However, most JPA usage I have seen out there has clearly favored the use of annotations. The fact that the specification documentation presents all of its examples using annotations and no examples of XML-based mapping samples (it only shows you an XML Schema) is likely one of the overriding reasons this is so. Object Relational Mapping was created to abstract details of the underlying database from the Java object model. However, JPA may be letting database details quickly creep back into the Java source. In this commentary, I will examine the various query styles in JPA, rationalize why they exist, and explain why certain styles (such as named queries) just make no sense to be annotated. I will conclude by showing that this small example is really part of a much larger problem.


Accessing data in JPA

Let me quickly go through the various ways of accessing data with JPA, assuming you are familiar with how to map Java objects in JPA. (For more thorough details, see Resources.)

EntityManager

Applications interact with Java objects at run time. Through the use of a special object called the entity manager, applications can query for, or persist, objects. An EntityManager instance is associated with a persistence context. Within the persistence context, the entity instances and their lifecycles are managed. You can think of the EntityManager as a facade to the underlying persistence mechanism. The EntityManager contains the necessary methods for accessing data. The simplest way to access persistent data is by using the find method. Here is an example of an application using the entity manager to find an object by its primary key:

Customer customer = (Customer)em.find(Customer.class,customerId);

The find method requires you to know the primary key as well as the actual class type.

JPA-QL queries

JPA also has a fully functional query language that can work against an object model. The JPA query language contains much functionality for more complex queries. Queries can be passed in dynamically to the entity manager:

Query q = em.createQuery("SELECT c FROM Customer c WHERE c.name LIKE
     :custName");
q.setParameter("custName", name);
q.setMaxResults(10);
List result = q.getResultList();

The ability to pass in queries at run time is necessary for some dynamic cases, such as unknown conditions. However, in most cases, you want to lock down your queries based on thorough performance testing.

Native queries

JPA also enables you to use native SQL queries against the underlying tables, and provides the ability to map back the results:

Query q = em.createNativeQuery(
"SELECT o.id, o.quantity, o.item, i.id, i.name, i.description "+
"FROM Order o, Item i " +
"WHERE (o.quantity > 25) AND (o.item = i.id)",
"OrderItemResults");

There are many cases where standard SQL is necessary. I gave many reasons in a previous Comment lines column.

Named queries

In most cases, you want to define well known queries that can be reused. Named queries enable you to define a query in a single location. There are many benefits to named queries:

  1. Externalizing or separating the query enables some flexibility in changing the query. This can be useful for fine tuning queries and locking them down.
  2. You can use the query from multiple locations.
  3. You can associate queries with well known business names.

You can define a named query as an annotation and execute it somewhere else in the code. Here is an example of using executing this type of named query:

Query allOrders = em.createNamedQuery("getAllOrder");
allOrders.setParameter("customerId",customerId);
Collection <CustomerOrder>  results = allOrders.getResultList();
return results;

Below is an example of how you can define a named query and associate it with an entity class:

@Entity
@Table(name="ORDER")
@NamedQuery(
     name="getAllOrder",
     queryString="SELECT OBJECT(o) FROM CustomerOrder as o WHERE
          o.customerId = :customerId")
public class CustomerOrder implements Serializable {


Problems with annotated named queries

Wait a minute!! Didn't I just say that a named query is a great way to externalize queries? Although defining a named query as an annotation can make it more reusable in the code, the benefit is so minimal that it is essentially useless. I can easily wrap the named query in a method and execute it again and again. The major benefit is the externalization and, therefore, an annotated named query makes little sense, if any.

A NamedQuery can also be defined using XML metadata:

<named-query name="getAllOrders" queryString="SELECT OBJECT(o) 
FROM CustomerOrder as o WHERE o.customerId = :customerId" />

Now, I can easily make changes to named queries without changing my source code and recompiling. Thinking back to my CMP days, the fact that the query was in the XML deployment descriptor made this factor insignificant. All the queries were defined in a single ejb-jar.xml file that was packaged in an EJB JAR that was further packaged in the EAR. The amount of work to change the JPA-QL in the XML file was still significant. (Martin Fowler also makes this point.)

Although the idea of alternative descriptors was available, the monolithic metadata made it an unattractive option.

JPA has three import differences:

  • Mapping files can be packaged in any JAR (or even packaged loosely) as long as they are available in the classpath.
  • You can have as many mapping files as you want. You can design to have related entities mapped in one XML file, or you can separate them out. You can keep all your queries in one separate XML file. This is something I would recommend.
  • Finally, you can have named native queries, and map the results back to a POJO.

For example, any queries you suspect can be changed can be placed externally. Consider the amount of wasted time spent if I had to change the source, perform a build, and install the application. If I could just change the query and restart, then I can test much quicker. Being able to change queries quickly during performance testing can drastically reduce test cycles.

Another example is when an ISV, selling software, needs to fine tune a query for a product they are selling to operate with a particular database vendor chosen by the customer. For example, changing the ordering of the where clause statement to improve performance helps in certain databases. Not being able to change queries could be a deal-breaker.

The ability to use native named queries is even more powerful, because you can map complex queries using native SQL back to a POJO, even if they are already mapped by something else. Look at this example:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm jpa.xsd ">

<named-native-query name=" OrderItemResults " query="SELECT o.id, 
     o.quantity, o.item, i.id, i.name, i.description FROM Order o, Item i WHERE 
     (o.quantity > 25) AND (o.item = i.id)" result-class="" 
     result-set-mapping="OrderItemResults" />

<sql-result-set-mapping name="">
     <entity-result entity-class="com.acme.Order.class">

<!--       <field-result column="" name=""/>
     <etc..> -->

     </entity-result>
     <entity-result entity-class="com.acme.Item.class">

<!--        <field-result column="" name=""/>
     <etc..> -->

     </entity-result>
</sql-result-set-mapping>

</entity-mapping>

In this above code, even if the Order or Item class is mapped with an annotation, a separate XML file, or not at all (I would then have to map each field), I can still override it with this XML. With JPA native queries, I can use JPA as a JDBC framework, similar to IBatis, when I need the flexibility of SQL. (I will show examples of this in my blog and future articles.)


A much larger problem

I have chosen to use named queries to illustrate a much larger problem. Object relational domain mapping (ORM) was created as an abstraction enabling applications to use object-oriented technology to address their domain. ORMs abstract out the database. Annotated mappings defeat the purpose of such an abstraction. Although such abstractions are not desired all the time, it is appropriate for many cases.

The JPA specification in itself is very attractive. JPA supports mapping your domain model using external XML mapping files. However, there is very little in the way of examples and documentation out there to show this. The specification committees are especially guilty, since they only provide annotated examples, implying that annotation is the preferred mechanism for JPA. However, there are valid reasons that suggest mapping files may be the preferred mechanism:

  • Client applications may share the source code, and therefore have intimate knowledge of the underlying database. Clients may even be tempted to perform SQL operations on their own.

  • What if I want to map objects to multiple databases? Although I can use XML to override annotations, if an object has a few mappings then I may not want to favor any. In an SOA environment, where I may be shipping objects across an ESB, each service may want to have a mapping to its own environment.

  • If my database changes -- even if I use XML to override the mappings -- my source code does not semantically map the underlying mapping. Anyone not aware of the XML override can make mistakes and assumptions. Information -- like the database schema name - changes all the time during a normal deployment process, and having this in the source would definitely hinder deployment.

Annotations do make things easier for a developer, but simplicity comes with trade-offs. I think the JPA writers of the world are generally doing the community a disservice by not documenting clear examples of using the XML style of mapping for JPA.


Conclusion

Named queries are just one example of how annotations can be overused. Annotations serve many useful purposes, but I fear there is much abuse of them in the ORM space. In my blog, I will spend sometime showing examples of using the XML mapping style of JPA.


Acknowledgements

Thanks to Keys Botzum and Tom Alcott for their comments.


Resources

About the author

Roland Barcia is a Certified IT Specialist for IBM Software Services for WebSphere in the New York/New Jersey Metro area. He is a co-author of IBM WebSphere: Deployment and Advanced Configuration. For more information on Roland, visit his Web site.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Java technology
ArticleID=108353
ArticleTitle=Comment lines: Roland Barcia: How useful are annotated named queries in the Java Persistence API, really?
publish-date=04192006
author1-email=barcia@us.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers