The Java EE 5 Tutorial

Chapter 25 Persistence in the Web Tier

This chapter describes how to use the Java Persistence API from web applications. The material here focuses on the source code and settings of an example called bookstore, a web application that manages entities related to a book store. This chapter assumes that you are familiar with the concepts detailed in Chapter 24, Introduction to the Java Persistence API.

Accessing Databases from Web Applications

Data that is shared between web components and is persistent between invocations of a web application is usually maintained in a database. Web applications use the Java Persistence API (see Chapter 24, Introduction to the Java Persistence API) to access relational databases.

The Java Persistence API provides a facility for managing the object/relational mapping (ORM) of Java objects to persistent data (stored in a database). A Java object that maps to a database table is called an entity class. It is a regular Java object (also known as a POJO, or plain, old Java object) with properties that map to columns in the database table. The Duke’s Bookstore application has one entity class, called Book that maps to WEB_BOOKSTORE_BOOKS.

To manage the interaction of entities with the Java Persistence facility, an application uses the EntityManager interface. This interface provides methods that perform common database functions, such as querying and updating the database. The BookDBAO class of the Duke’s Bookstore application uses the entity manager to query the database for the book data and to update the inventory of books that are sold.

The set of entities that can be managed by an entity manager are defined in a persistence unit. It oversees all persistence operations in the application. The persistence unit is configured by a descriptor file called persistence.xml. This file also defines the data source, what type of transactions the application uses, along with other information. For the Duke’s Bookstore application, the persistence.xml file and the Book class are packaged into a separate JAR file and added to the application’s WAR file.

As in JDBC technology, a DataSource object has a set of properties that identify and describe the real world data source that it represents. These properties include information such as the location of the database server, the name of the database, the network protocol to use to communicate with the server, and so on.

An application that uses the Java Persistence API does not need to explicitly create a connection to the data source, as it would when using JDBC technology exclusively. Still, the DataSource object must be created in the Application Server.

To maintain the catalog of books, the Duke’s Bookstore examples described in Chapters Further Information about Web Applications through Including the Classes, Pages, and Other Resources use the Java DB evaluation database included with the Application Server.

To populate the database, follow the instructions in Populating the Example Database.

To create a data source, follow the instructions in Creating a Data Source in the Application Server.

This section describes the following:

Defining the Persistence Unit

As described in Accessing Databases from Web Applications, a persistence unit is defined by a persistence.xml file, which is packaged with the application WAR file. This file includes the following:

The jta-data-source element indicates that the transactions in which the entity manager takes part are JTA transactions, meaning that transactions are managed by the container. Alternatively, you can use resource-local transactions, which are transactions controlled by the application itself. In general, web application developers will use JTA transactions so that they don’t need to manually manage the life cycle of the EntityManager instance.

A resource-local entity manager cannot participate in global transactions. In addition, the web container will not roll back pending transactions left behind by poorly written applications.

Creating an Entity Class

As explained in Accessing Databases from Web Applications, an entity class is a component that represents a table in the database. In the case of the Duke’s Bookstore application, there is only one database table and therefore only one entity class: the Book class.

The Book class contains properties for accessing each piece of data for a particular book, such as the book’s title and author. To make it an entity class that is accessible to an entity manager, you need to do the following:

The following code shows part of the Book class:

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="WEB_BOOKSTORE_BOOKS")

public class Book implements Serializable {

    private String bookId;
    private String title;

    public Book() { }

    public Book(String bookId, String title, ...) {
        this.bookId = bookId;
        this.title = title;
        ...
    }

    @Id
    public String getBookId() {
        return this.bookId;
    }

    public String getTitle() {
        return this.title;
    }
    ...

    public void setBookId(String id) {
        this.bookId=id;
    }

    public void setTitle(String title) {
        this.title=title;
    }
    ...
}

Obtaining Access to an Entity Manager

The BookDBAO object of the Duke’s Bookstore application includes methods for getting the book data from the database and updating the inventory in the database when books are sold. In order to perform database queries, the BookDBAO object needs to obtain an EntityManager instance.

The Java Persistence API allows developers to use annotations to identify a resource so that the container can transparently inject it into an object. You can give an object access to an EntityManager instance by using the @PersistenceUnit annotation to inject an EntityManagerFactory, from which you can obtain an EntityManager instance.

Unfortunately for the web application developer, resource injection using annotations can only be used with classes that are managed by a Java EE compliant container. Because the web container does not manage JavaBeans components, you cannot inject resources into them. One exception is a request-scoped JavaServer Faces managed bean. These beans are managed by the container and therefore support resource injection. This is only helpful if your application is a JavaServer Faces application.

You can still use resource injection in a web application that is not a JavaServer Faces application if you can do it in an object that is managed by the container. These objects include servlets and ServletContextListener objects. These objects can then give the application’s beans access to the resources.

In the case of Duke’s Bookstore, the ContextListener object creates the BookDBAO object and puts it into application scope. In the process, it passes to the BookDBAO object the EntityManagerFactory object that was injected into ContextListener:

public final class ContextListener implements SerlvetContextListener {
...
@PersistenceUnit
private EntityManagerFactory emf;

public void contextInitialized(ServletContexEvent event) {
    context = event.getServletContext();
    ...
    try {
        BookDBAO bookDB = new BookDBAO(emf);
        context.setAttribute("bookDB", bookDB);
    } catch (Exception ex) {
        System.out.println(
            "Couldn’t create bookstore database bean: "
                 + ex.getMessage());
    }
}
}

The BookDBAO object can then obtain an EntityManager from the EntityManagerFactory that the ContextListener object passes to it:

private EntityManager em;

public BookDBAO (EntityManagerFactory emf) throws Exception {
    em = emf.getEntityManager();
    ...
}

The JavaServer Faces version of Duke’s Bookstore gets access to the EntityManager instance a little differently. Because managed beans allow resource injection, you can inject the EntityManagerFactory instance into BookDBAO.

In fact, you can bypass injecting EntityManagerFactory and instead inject the EntityManager directly into BookDBAO. This is because thread safety is not an issue with request-scoped beans. Conversely, developers need to be concerned with thread safety when working with servlets and listeners. Therefore, a servlet or listener needs to inject an EntityManagerFactory instance, which is thread-safe, whereas a persistence context is not thread-safe. The following code shows part of the BookDBAO object included in the JavaServer Faces version of Duke’s Bookstore:

import javax.ejb.*;
import javax.persistence.*;
import javax.transaction.NotSupportedException;
public class BookDBAO {

    @PersistenceContext
    private EntityManager em;
...

As shown in the preceding code, an EntityManager instance is injected into an object using the @PersistenceContext annotation. An EntityManager instance is associated with a persistence context, which is a set of entity instances that the entity manager is tasked with managing.

The annotation may specify the name of the persistence unit with which it is associated. This name must match a persistence unit defined in the application’s persistence.xml file.

The next section explains how the BookDBAO object uses the entity manager instance to query the database.

Accessing Data from the Database

After the BookDBAO object obtains an EntityManager instance, it can access data from the database. The getBooks method of BookDBAO calls the createQuery method of the EntityManager instance to retrieve a list of all books by bookId:

public List getBooks() throws BooksNotFoundException {
    try {
        return em.createQuery(
            "SELECT bd FROM Book bd ORDER BY bd.bookId").
                getResultList();
    } catch(Exception ex){
        throw new BooksNotFoundException("Could not get books: "
             + ex.getMessage());
    }
}

The getBook method of BookDBAO uses the find method of the EntityManager instance to search the database for a particular book and return the associated Book instance:

public Book getBook(String bookId) throws BookNotFoundException {
    Book requestedBook = em.find(Book.class, bookId);
    if (requestedBook == null) {
        throw new BookNotFoundException("Couldn’t find book: "
             + bookId);
    }
    return requestedBook;
}

The next section describes how Duke’s Bookstore performs updates to the data.

Updating Data in the Database

In the Duke’s Bookstore application, updates to the database involve decrementing the inventory count of a book when the user buys copies of the book. The BookDBAO performs this update in the buyBooks and buyBook methods:

public void buyBooks(ShoppingCart cart) throws OrderException{
    Collection items = cart.getItems();
    Iterator i = items.iterator();
    try {
        while (i.hasNext()) {
            ShoppingCartItem sci = (ShoppingCartItem)i.next();
            Book bd = (Book)sci.getItem();
            String id = bd.getBookId();
            int quantity = sci.getQuantity();
            buyBook(id, quantity);
        }
    } catch (Exception ex) {
        throw new OrderException("Commit failed: "
             + ex.getMessage());
    }
}
public void buyBook(String bookId, int quantity)
     throws OrderException {
    try {
        Book requestedBook = em.find(Book.class, bookId);
        if (requestedBook != null) {
            int inventory = requestedBook.getInventory();
            if ((inventory - quantity) >= 0) {
                int newInventory = inventory - quantity;
                requestedBook.setInventory(newInventory);
            } else{
                throw new OrderException("Not enough of "
                     + bookId + " in stock to complete order.");
            }
        }
    } catch (Exception ex) {
        throw new OrderException("Couldn’t purchase book: "
             + bookId + ex.getMessage());
    }
}

In the buyBook method, the find method of the EntityManager instance retrieves one of the books that is in the shopping cart. The buyBook method then updates the inventory on the Book object.

To ensure that the update is processed in its entirety, the call to buyBooks is wrapped in a single transaction. In the JSP versions of Duke’s Bookstore, the Dispatcher servlet calls buyBooks and therefore sets the transaction demarcations.

In the following code, the UserTransaction resource is injected into the Dispatcher servlet. UserTransaction is an interface to the underlying JTA transaction manager used to begin a new transaction and end a transaction. After getting the UserTransaction resource, the servlet calls to the begin and commit methods of UserTransaction to mark the boundaries of the transaction. The call to the rollback method of UserTransaction undoes the effects of all statements in the transaction so as to protect the integrity of the data.

@Resource
UserTransaction utx;
...
try {
    utx.begin();
    bookDBAO.buyBooks(cart);
    utx.commit();
} catch (Exception ex) {
    try {
        utx.rollback();
    } catch (Exception exe) {
        System.out.println("Rollback failed: "+exe.getMessage());
}
...