Design Choices in a Web-only Application Using Java Persistence

Status: In Early Access
Sean BrydonSmitha Kangath

Problem Description

The new Java Persistence APIs provides a new way to design the model tier of web applications. The specifications for Java EE 5 including Java Persistence are long and cover a lot of techologies. Since developers face a lot of choices when designing a Web application that uses the Java Persistence API, this document will briefly cover some of the key programming model choices.  It will provide an overview of considerations when using entity managers, entities, transactions, and  leveraging annotations.

It is useful to first define and scope what we mean by a "web-only" application. By a web-only application, we mean an application packaged as a war file (which may sometimes be packaged in a ear file), no EJB modules, and deployed on an application server that is compatible with the Java EE 5 platform.  This document doesn't cover the case of EJBs, such as session beans, acting as clients of the entities. The web-only application would have web components such as Servlets using the Java Persistence APIs. It would have all the services of a Java EE container available, such as the JNDI naming service, security services, and so on. This is a portable web application capable of running on any Java EE 5 compatible application server. In other documents, we will later cover other application architectures, such as a web application using EJBs with Java Persistence and a web application deployed on a Web container in addition to Java Persistence in a Java SE environment (Java Persistence is capable of running without a Java EE container).  Note that for a web-only application the programming model is different than for a web application with an EJB module. The choices and constraints outlined in this document pertains only to the programming model for web-only applications.

When designing a web application using the Java Persistence APIs, here are some design considerations:
Since these choices and many others impact the design of the application, let's consider them in more detail and uncover the programming model for using Java Persistence in web applications.

Solution

Within a persistence context, the entity instances and their lifecycle are managed by the entity manager. There are two types of entity managers, container-managed and application-managed. The main programming model choice for a web-only application is choosing to use container-managed or application-managed entity managers. Why does the choice matter? For one thing, application-managed requires more coding in the logic of the application, as the aplication must programmatically manage the lifecycle of the entity manager. With container-managed the lifecycle of the entity managers is transparently done by the Java EE container. But the choice also matters as there are constraints and tradeoffs in each model. Table 1 summarizes the two different options.
 
Application-Managed Entity Managers Container-Managed Entity Managers
Specified by using the entity manager factory to obtain an entity manager instance Specified by using @PersistenceContext annotation or JNDI lookup to obtain an entity manager instance
Must use extended-scope persistence context Must use transaction-scoped persistence context
Must use application-managed transactions Must use application-managed transactions
Table 1: Characteristsics of Application-Managed and Container-Managed Entity Managers

Specifying the choice of container-managed or application-managed entity manager is done in the application code. If the application code uses JNDI lookup or dependecy injection with the @PersistenceContext annotation to obtain an EntityManager instance, this results in container-managed entity managers. If the application code uses the  EntityManagerFactory.createEntityManager method to get an entity manager, this results in application-managed entity managers.

One impact of choosing container-managed or application-managed entity managers is that each is constrained to a type of persistence context lifetime, either transaction-scoped or extended persistence context. A persistence context can be transaction-scoped or have an extended scope that spans multiple transactions.  For web-only applications, an application-managed entity manager has an extended scope and a container-managed entity manager has a transaction scope. One design question is whether the lifetime of a persistence context should be scoped to a single transaction or whether it should span multiple transactions. A transaction-scoped persistence context exists for the duration of the current JTA transaction. An extended persistence context spans across transaction boundaries. This is mostly used in scenarios where you don't want to keep an entity manager active between several HTTP requests as in a session. If an application must use extended-scoped transactions, then it must use application-managed entity managers. Note, for web applications using EJBs, extended-scoped transactions can be used with both container and application-managed entity manangers. But in a web-only application, you can only use extended-scoped transactions with application-managed entity managers.

In a web-only application, since container-managed entity managers can only use transaction-scoped persistence contexts, there is no need to specify the type of persistence context. Note, this is different from a web application with EJBs, where an entity manager is specified to use extended-scoped persistence context by @PersistenceContext(type=PersistenceContextType.EXTENDED) or @PersistenceContext(type=PersistenceContextType.TRANSACTION). In a web-only application, the code should not attempt to set the persistence context type with annotations.

One constraint to be aware of for web-only applications is that you can only use application-managed transactions. Web applications with an EJB module using stateless and stateful session beans to access the entities can use container-managed transactions. Container-managed transactions allow the Java EE platform to handle the lifecycle of transactions. But for web-only applications, container-managed transactions are not an option. This means you will be using programmatic APIs to demarcate the starting, committing, and managing of transactions.

Now let's take a look at these two different styles of using Java Persistence in a web-only application: using application-managed entity manager and using container-managed entity manager.

1. Using Application-Managed Entity Manager

Let's take a look at the design of  an application using application-managed entity managers. The general usage of an entity manager factory is to inject one using the PersistenceUnit annotation. For example,  @PersistenceUnit(unitName="CatalogPu") EntityManagerFactory emf.Then the code should hold it for re-use until done using its entity managers. The EntityManagerFactory is threadsafe so can be kept at application scope, so it is recommended that web-only applications using application-managed entity managers cache it at application scope and share it among requests. Note that if an application closes an entity manager factory then all its entity managers are considered in the closed state.

Let's look at the EntityManager interface for application-managed entity managers. The EntityManager interface has methods to persist objects and execute queries etc, and it has some lifecycle methods your code must call since you are not using container-managed entity managers.  First let's look at how to obtain an entity manager for application-managed entity managers. When using an application-managed entity manager, the code uses the EntityManagerFactory.createEntityManager() method to get an entity manager.  Usage of the factory to obtain entity managers also specifies that these entity managers will be managed by the application. For application-managed entity managers you can not use injection or JNDI lookups to obtain the entity manager and instead MUST use the EntityManagerFactory.createEntityManager().

Code  example 1 shows the proper way to obtain an entity manager factory. Note, the reference emf can be kept as an instance variable and re-used on other method calls. The usage of application-managed entity managers is specified by using the factory to obtain entity managers with the call to EntityManager em = emf.createEntityManager(). The entity manager APIs can then be used to persist or query entities.

@PersistenceUnit private EntityManagerFactory emf;//declared as instance variable for re-use.
...
//this method uses entity manager to persist an entity Item.java
public void addItem(Item item){
  EntityManager em = emf.createEntityManager();
  try{
    ...
    em.persist(item);
  ...
  } finally {
    em.close();
  }

Code Example 1: Obtaining and Using application-managed entity managers

The application code must also manage the lifecycle of the entity managers. When using application-managed entity managers the code uses the methods close, isOpen, and  joinTransaction to manage entity managers and their lifecycle. The application needs to close the entity manager when finished using it. The EntityManager is not thread-safe so you should not hold references to instances of an EntityManager in a manner that is not threadsafe.  Since application-managed entity managers have extended-scoped persistence contexts, the code can choose to leave the entity manager open for several requests and close it when done. Application-managed entity managers can also mimic the behavior of transaction-scoped entity managers and close the entity manager at the end of the same method that created it. In this way, extended-scoped persistence contexts offer more flexibililty as they can achieve the behavior of transaction-scoped persistence contexts as well as span multiple HTTP requests and multiple transactions. One use case for leaving an entity manager open and using extended-scoped persistence contexts is for multi-page forms which might use several form submits to gather all the user information in the entities, then once it has all gathered all the entities can be flushed and committed to the database.

Some of the methods in the javax.persistence.EntityManager  interface require that they must be invoked within a transaction context. These methods throw a TransactionRequiredException if there is no transaction and the persistence context is transaction-scoped. These methods do writes or updates to the data in the database. The developer should make sure there exists a transaction context before calling these methods.  These methods are the persist, merge, remove, flush, and refresh methods.

Another design choice for application-managed entity managers is whether to use JTA or local-resource transaction. Web-only applications must use application-managed transactions. This means the code will be using programmatic APIs to demarcate the starting and committing of transactions. But for application-managed entity managers, you have some extra flexibility that you do not get with container-managed entity managers. With application-managed entity managers there is the design decision of whether to use a JTA entity manager or a resource-local entity manager. A JTA entity manager's transactions are controlled through JTA. Application-managed entity managers can be either JTA or resource-local.  Code example 2 compares the code for a JTA application-managed entity manager to the code for a resource-local application-managed entity manager.

Code for JTA entity managers Code for resource-local entity manager 
@PersistenceUnit EntityManagerFactory emf;
@Resource UserTransaction utx;
 
...
public void addItem(Item item){
  EntityManager em = emf.createEntityManager();
  try{
    utx.begin();
    em.joinTransaction();
    em.persist(item);
    utx.commit();
  } catch(Exception exe){
    System.out.println("Error persisting item: "+exe);
    try {
      utx.rollback();
    } catch (Exception e) {}
 } finally {
    em.close();
 }
}
...
@PersistenceUnit private EntityManagerFactory emf;
...
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//call other entity manager operations like persist
...
em.getTransaction().commit();
em.close();
Code Example 2: Comparison of Code for JTA Entity Managers or Resource-local Entity Managers

In general, using JTA is the preferred choice. Using JTA allows more than one operation to be inside the transaction demarcation and be atomic. Using resource-local may have some slight performance benefits, could be useful in simple cases where just one operation is done, and could be useful if you explicitly wanted two operations in a method to be on independent transcations.

2. Using Container-Managed Entity Manager

A web application can specify that it will use container-managed entity managers by using JNDI lookups to get an entity manager or by using the @PersistenceContext annotation to inject an entity manager, and this results in the entity manager being managed by the Java EE 5 container. For container-managed entity managers, the application code does not interact with the entity manager factory, instead it just uses injection or lookup. With a container-managed entity manager, the container calls the lifecycle methods for action such as closing of the EntityManager instance. This makes the code a bit simpler. The code just injects an entity manager and uses it, and at the end of the calling method the container closes the entity manager.

Note that entity managers are not thread safe so code should not try to hold them and reuse them. Instead an entity manager should be created and closed within the same method. If an EntityManager is declared as an instance variable for example in a Servlet, then multiple concurrent requests might simultaneously access the entity manager and result in behavior that is not threadsafe. There are exceptions to this, for example request-scoped web components such as a JSF managed bean specified with request scope could  declare an entity manager as an instance variable and injection could be used to set its value. In this case of a JSF managed bean with request scope, as the object is destroyed at the end of the request the entity manager wont be reused and is threadsafe. But that is an unusual case and leaves one to consider a more general solution. The inability to use annotations in all web components to inject the entity managers in a threadsafe style makes it a bit tricky to use the container-managed entity managers in a web-only application. Since entity managers are not thread safe,  most web components should declare EntityManagers within the scope of a method. But annotations can not be applied inside of methods, so if you declare an EntityManager inside of a method, then you can not use injection. This means you have to use JNDI lookups to obtain entity manager instances. The example code below shows how to use an annotation to declare the dependency using annotations, which avoids using a deployment descriptor, and then how to use JNDI lookup to get an entity manager.  Thread safe way to use entity managers in web-only (no EJB modules) with container-managed entity managers. Code example 3 shows the alternative ways to use an entity manager, showing the proper threadsafe way and the bad practice

Bad Practice: Not Threadsafe, so a bad idea

Good Practice: Threadsafe.
Declares JNDI dependency at top with annotation. Using JNDI Lookup to get entity manager.
public class MyServlet extends HttpServelet {

  @PersistenceContext EntityManager em;
 
  public void doGet( HttpServletRequest req,
     HttpServletResponse resp) throws ... {
    ...
    //note, a concurrent thread
    //could be using
the same
    //entity manager instance
    em.persist(item)
    
....some code  using the entity manager
  }
}
 


@PersistenceContext(name="foo", unitName="myPuName")
public class MyServlet extends HttpServelet {

  public void doGet(HttpServletRequest req,
                    HttpServletResponse resp) throws ... {
    ...
    //each doGet method gets its own 
    //
entity manager so this method-scoped 
    //
em is thread safe.
    //
also notice JNDI lookup to get em
    EntityManager em = (EntityManager) ic.lookup("java:comp/env/foo");
    em.persist(item)
    
....some code  using the entity manager
  }
}

Code Example 3: Threadsafe Way to Use Container-managed Entity Managers in Web-only Applications with No EJB Modules

A web-only application (no EJB modules) can not have container-managed extended persistence context, so when using container-managed entity managers only transaction-scoped persistence contexts can be used. This is because there is no way for the container to call close() as in stateful session bean. If extended-scoped persistence contexts are required then consider using application-managed entity managers or introducing an EJB module and using a Stateful Session Bean which allow container-managed entity managers with extended-scope persistence contexts.

According to the Java Persistence specification, a container-managed entity manager has to be a JTA entity manager. A JTA entity manager participates in the current JTA transaction. Unlike the application-managed entity managers, local-resource transactions can not be used. This means that web-only applications will need to use JTA in the application code to demarcate transactions. Code example 4 shows the proper way to use container-managed entity managers with JTA to demarcate the transactions.

@PersistenceContext(name="foo", unitName="myPuName")
public class MyServlet extends HttpServelet {

@Resource UserTransaction utx;

  public void doGet(HttpServletRequest req,
                    HttpServletResponse resp) throws throws ServletException, IOException {

    EntityManager em = (EntityManager) ic.lookup("java:comp/env/foo");

    //get data from request
    String name = request.getParameter("item_name");
    ...
    //create new Item entity
    Item item = new Item();
    //set values of Item entity
    item.setName(name);
    ...
    utx.begin();
    em.persist(item); //persist and add new Item to database
    utx.commit();
    ...
}

Code Example 4: Showing Proper Way to Use Container-managed Entity Managers in a Web-only Application

As shown in the code example 4, using a container-managed entity manager is fairly easy. Since the entity manager is container-managed, there are no explicit calls to manage its lifecycle. The @Resource annotation injects a UserTransaction instance that is used to control JTA transactions. UserTransaction is threadsafe so can be held as an instance variable that is shared by other requests. Since we are doing a database write here, we start a transaction just before calling em.persist() and commit it afterwards. The container manages the lifecycle of the entity manager instance and closes it at the end of the method. Notice that Servlet class has an annotation  @PersistenceContext(name="foo", unitName="myPuName") which is used to declare a dependency on the persistence context, and then in the Servlet.doGet method uses JNDI to lookup and get an entity manager with  EntityManager em = ic.lookup("java:comp/env/foo").

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.