Tuesday, September 7, 2010

Where to put named queries in JPA?

JPA provides multiple ways to obtain entities. There is a very simple programmatic API that allows us to get an entity by ID, there's a more elaborate one called the Criteria API and then there's a query language called JPQL (Java Persistence Query Language).

JPQL is an object oriented query language that is based on the simplicity of SQL, but works directly on objects and their properties. The problem with such a language that's used inside another language (Java) is where to store the query definition. Traditionally there have been 2 solutions:

  1. Store the definitions in annotations on some entity.
  2. Construct strings holding the definitions inline in your Java code.

The first solution is called a Named Query in JPA, it looks like this:

@NamedQueries(value={
   @NamedQuery(
      name = "Website.getWebsiteByUserId",
      query="select website from Website website where website.userId = :userId")
   @NamedQuery(...)
})
@Entity
public class Website { ... }

The advantages of this method are twofold: JPA checks your query is valid at startup time (no runtime surprises) and the query definition is parsed only once and re-used afterwards. As an extra bonus, it also strongly encourages to used named parameters. The disadvantage however is that its location is just plain awkward. The entity is typically not the location where we wish to store this kind of logic. It gets even more awkward when the query is about multiple entities, yet you have to choose a single one to store the query definition on. Instead, a DAO, Service or whatever code is used to interact with the entity manager is a much more logical place to store a query definition. Unfortunately, @NamedQuery only works on entities. Neither Enterprise beans nor any other kind of managed bean in Java EE supports them.

This thus brings us to the second solution, which looks like this:

public foo() {
    // some code
    Website website = entityManager.createQuery(
      "select website from Website website where" website.userId = :userId", Website.class)
      .setParameter("userId", userId)
      getSingleResult();

This is arguably a much better location, though still not ideal. If the query is long, we have to concatenate strings which makes the query hard to read and hard to maintain. It has the major disadvantage that the query is only checked at runtime and has to be re-parsed over and over again. There are some limited opportunities for reusing a Query object obtained by createQuery(), but since this object is only valid as long as the persistence context in which it was created is still active, those opportunities are really rather limited. Additionally, this style of query definition can make it tempting for developers to build their queries dynamically, giving rise to some nasty potential injection holes.

So having the choice between those two, which one do we choose? Actually, it appears there is a third solution, which is for some reason quite often overlooked by many people:

  1. Store the query text in XML (mapping) files.

In addition to annotations, JPA (and pretty much every API in Java EE that uses annotations) allows you to define the same thing or occasionally a little more in XML. Of course we don't want one huge XML file with all our queries, but as it turns out JPA simply allows us to use as many files as we need and organize them in whatever way we want. We could for example put all queries related to some entity in one file anyway, or put all financial queries in one file and all core queries in another file, etc. The mechanism is actually quite similar to using multiple faces-config.xml files in JSF. The XML based solution looks as follows.

persistence.xml:

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

 <persistence-unit name="somePU">
  <jta-data-source>java:/someDS</jta-data-source>

  <!-- Named JPQL queries per entity, but any other organization is possible  -->
  <mapping-file>META-INF/jpql/Website.xml</mapping-file>
  <mapping-file>META-INF/jpql/User.xml</mapping-file>
        </persistence-unit>
</persistence>

Website.xml:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="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 http://java.sun.com/xml/ns/persistence/orm_1_0.xsd "
>
 <named-query name="Website.getByUserId">
  <query>
   SELECT 
    website
   FROM 
    Website website
   WHERE
    website.userId = :userId
  </query>
 </named-query>

 <named-query name="Website.foobar">
  <query>
   ...
  </query>
 </named-query>

</named-query>
</entity-mappings>

Finally, using such query definitions in code is exactly the same as if they would have been defined using annotations, i.e. by calling entityManager.createNamedQuery().

Each XML file can contain as few or as many queries as you like. It might make sense to put a really complicated and huge query in one file, but to group several smaller related queries in another file. Do note that query names are part of a global namespace and are not automatically put in any namespace based on the file they are defined in. In the example above queries are pre-fixed with "Website.", which happens to be the name of the entity but you can choose anything you want here. In the example META-INF/jpql was used as the directory to store queries, but any location on the class path including storing queries in jars will do.

As mentioned, for some reason this XML method seem to be often overlooked by many people. I've personally met multiple persons who build themselves a management system for storing JPQL queries in files, loading them, substituting parameters, etc while such a mechanism is in fact readily available in JPA (and has been since JPA 1.0!). Of course, the home grown systems don't have the startup-time validation of queries nor do they do any pre-parsing and pre-compilation of queries.

Arjan Tijms

Wednesday, April 28, 2010

Facelets and legacy JSP

It's well known that Facelets is the far superior technology when it comes to authoring pages using JSF. By default, Facelets has no provisions to include content from JSP pages, or Servlets for that matter. Normally this really isn't needed. Facelets provides a better and clearer templating mechanism than what JSP has ever offered.

Facelets & JSP in one app

However, when migrating a large application you might have to run a while in mixed-mode, that is running with both Facelets and JSP pages in a single application. It ain't pretty, but it is explicitly supported. Ever wondered why you are forced to use prefix mapping for this? Well, the Facelets view handler delegates to the default view handler for createView, which transforms a view ID suffix like .jsf or .xhtml to a default one (e.g. .jsp), but leaves the suffix alone when using a prefix mapping like /faces/*. The Facelets viewhandler later decides to handle a request itself or delegate again to the default view handler, based on the suffix. When using suffix mapping, the first delegation had just caused this suffix to be replaced with the default suffix and the Facelets view handler thus can't make any distinction between a Facelets request and a JSP request anymore.

The above is thus why you have to use prefix mapping for Facelets pages if you want to use both Facelets and JSP in a single application.

JSP includes

Having Facelets and JSP pages in 1 application might be only half of the story. It's not unlikely that the existing JSP pages make use of included resources that are also JSP based, or maybe Servlet based. Rewriting these to full-fledged Facelets artifacts is technically the best option, but if you have to run in mixed-mode for a while, this means you have to maintain two versions of these includes. Obviously maintaining two separate versions is not ideal too.

One alternative, that I like to present here, is building a bridge component that executes the JSP or Servlet and writes its output to the standard JSF response writer. Thanks to Facelets and JSF in general, such a component is actually not that hard to build:

The component's source code:

public class JSPIncludeComponent extends UIComponentBase {

public String getFamily() {
   return "components.jsp.include";
}

public void encodeBegin(FacesContext context) throws IOException {
   try {
      ExternalContext externalContext = context.getExternalContext();
      HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
      HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

      // Create dispatcher for the resource given by the componen's page attribute.
      RequestDispatcher requestDispatcher = request.getRequestDispatcher((String) getAttributes().get("page"));

      // Catch the resource's output.
      CharResponseWrapper responseWrapper = new CharResponseWrapper(response);
      requestDispatcher.include(request, responseWrapper);

      // Write the output from the resource to the JSF response writer.
      context.getResponseWriter().write(responseWrapper.toString());
   }
   catch (ServletException e) {
      throw new IOException();
   }
}
}

The component here gets its content from the resource path denoted by the attribute "page". To JSF, there is no difference between content that is generated locally in the component and content that the component obtained from somewhere else.

The component makes use of a HttpServletResponseWrapper, which is a fairly common artifact in many legacy Servlet/JSP applications. For completeness, here is the source of the particular implementation I used:

public class CharResponseWrapper extends HttpServletResponseWrapper {

   private CharArrayWriter output;

   @Override
   public String toString() {
      return output.toString();
   }

   public CharResponseWrapper(HttpServletResponse response) {
      super(response);
      output = new CharArrayWriter();
   }

   public CharArrayWriter getCharWriter() {
      return output;
   }

   @Override
   public PrintWriter getWriter() {
       return new PrintWriter(output);
  }

   @Override
   public ServletOutputStream getOutputStream() {
      return new CharOutputStream(output);
   }

   public InputStream getInputStream() {
      return new ByteArrayInputStream( toString().getBytes() );
   }
}

class CharOutputStream extends ServletOutputStream {

   private Writer output;

   public CharOutputStream( Writer writer ) {
      output = writer;
   }

   @Override
   public void write(int b) throws IOException {
      output.write(b);
   }
}

In JSF 1.2 there's a minor inconvenience where you have to register the component you just created in an .XML file. JSF 2.0 provides a convenient annotation for this, but in JSF 1.2 it has to happen via XML:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2"
>      
 <component>
  <component-type>com.example.component.JSPIncludeComponent</component-type>
  <component-class>com.example.component.JSPIncludeComponent</component-class>
 </component>

</faces-config>

This can either be put in your regular faces-config.xml, or in a separate faces-config.xml in a META-INF directory on your class-path. The latter option is typically when packaging in a jar, but also works for any regular Java source dir of your web module.

To make the component usable on a Facelets page, we only have to assign it a name space and a tag name. This has to happen in a file with a suffix .taglib.xml that's also in a META-INF directory on your class-path. Even in JSF 2.0, there are unfortunately no annotations for this. For this example I called the file foo-include.xml:

<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">

<facelet-taglib> 
 <namespace>http://foo.com/jsf</namespace> 

 <tag>
   <tag-name>include</tag-name>
   <component>
     <component-type>com.example.component.JSPIncludeComponent</component-type>
   </component>
 </tag>

</facelet-taglib>

Note that this example uses the older DTD for the standalone Facelets implementation for JSF 1.2. JSF 2.0 uses a more modern xsd, for which an example can be seen here. Also note that we don't have to define any attributes nor did we had to define any getters and setters for those attributes on the UI component implementation. This convenience does come at a cost though, as your IDE now can't provide you with any content assist for the component's attributes.

Finally an example of how it all comes together on an actual Facelets page:

<?xml version="1.0" encoding="UTF-8"?> 
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
   xmlns:ui="http://java.sun.com/jsf/facelets" 
   xmlns:h="http://java.sun.com/jsf/html"
   xmlns:foo="http://foo.com/jsf"
   version="2.1"
> 
 <ui:composition>
  <html xmlns="http://www.w3.org/1999/xhtml">

   <head>    
    <title>JSP include example</title>
    <meta http-equiv="pragma" content="no-cache"/>
    <meta http-equiv="cache-control" content="no-cache"/>
    <meta http-equiv="expires" content="0"/> 
   </head>  

   <body>    
    JSP: <br/>
    <foo:include page="/test.jsp"/><br/><br/>

    SERVLET: <br/>
    <foo:include page="/TestServlet"/><br/><br/>          
   </body>
  </html>

 </ui:composition> 

</jsp:root>

I wouldn't recommend using this as a lasting solution, but it might ease a migration from legacy JSP with smelly scriptlets and all on them to a more sane and modern Facelets application.

Arjan Tijms