Chapter 10. Rapid Seam development

This chapter covers

  • The Seam Application Framework
  • Building CRUD screens for an entity
  • Paginating and sorting a query result set
  • Assigning restrictions to queries

If you think back to when you first learned golf—perhaps you are still learning—you were probably overwhelmed with all of the things you have to concentrate on at once. To start, you have to judge the distance you want to hit the ball and choose the right club for it. This “design decision” is a tough call even for the best golfers. Once your aim is set and you’re ready to take your swing, you have to position your stance, set your grip, align the clubface, keep your head down, your shoulders level, and your eye on the ball. It’s all so mechanical that even a good swing feels unnatural.

Then, one day, it all clicks. You stop thinking about every last detail and you just swing. It’s hard to explain how you know—it just becomes natural to you, like walking or riding a bike. When you look down the fairway, accounting for the obstacles in front of you, you just get that sense of how far you want to send the ball and which club to use to get it there. You are no longer “designing” your decisions based on some prescribed distance chart, nor do you feel uncomfortable when you swing.

This chapter is the culmination of everything you’ve learned so far about Seam, and it’s your chance to make things click. You’ll be combining components, component instances, conversations, page parameters, page actions, navigation rules, managed persistence, and transactions to implement several new features in the Open 18 application. By the end of this chapter, developing with Seam should feel so natural that you’ll be able to assemble a custom application and still enjoy the fresh air. The key to increasing your productivity with Seam is learning about the component templates in the Seam Application Framework and knowing how to put them to use. These templates enable you to develop rapidly by handling repetitive and mundane tasks and putting Seam services close at hand. You’ll discover that these classes can be put to use by extending them in Java, configuring them in the component descriptor, or both. Let’s begin by exploring what this framework has to offer.

10.1. A framework within a framework

As you are well aware by now, Seam is an application framework for Java EE. It provides a container that manages components and leverages those components to tie the layers of an enterprise Java application together. Nestled within the Seam code base lies a handful of classes that comprise the Seam Application Framework. This “framework within a framework” is a specialized collection of component templates that effortlessly blanket the programming requirements of garden-variety web applications. Such tasks include performing create, read, update, and delete (CRUD) operations on entity instances; querying for data; and developing JSF page controllers. You may be hesitant to give this spattering of classes much notice, but I assure you that they’ll carry you a long way.

 

Note

The Seam reference documentation refers to this grouping of classes as the Seam Application Framework. The title is a potential cause of confusion, as it resembles the name of the broader Seam framework in which it resides. Therefore, whenever I talk about this set of classes and the functionality they provide, I address it using the proper noun Seam Application Framework to remain consistent with the documentation. I define it as a framework of classes for quickly building page controllers that perform CRUD and query operations on entities.

 

At the end of chapter 2, you had a CRUD application in place that you could use to impress your boss. Since then, you’ve made enhancements to the application to further demonstrate that your investment in Seam is paying off (hopefully convincing your boss to buy copies of this book for your coworkers). The trouble is, most of the inner workings of those original CRUD screens, which are powered by the Seam Application Framework, remain a mystery. You’ve learned how to manage the view-edit-save sequence correctly using conversations and the extended persistence context. You’ll now learn how this framework helps facilitate that use case. You’ll also discover that it can generate status messages that keep the user informed along the way.

You’ll see how the framework aids in creating pages that list entities of a particular type retrieved from the database. But it doesn’t just dump all the records at once, which could potentially be a costly operation. Instead, it provides support for truncating the list into pages. The component that manages the query responds to pagination and sorting commands, and helps you develop a search filter to allow the user to pare down the result set.

In this chapter, you’ll mirror the behavior and design of the screens created in chapter 2 to incorporate a new entity into the application, Round, which represents a round of golf. This time, you’ll build the functionality from the ground up and then take it several steps further. This exercise allows you to become familiar with the Seam Application Framework and shows you how to build on it. It should come as no surprise that the cornerstone of this framework is persistence.

10.1.1. Wrapping the persistence API

Database-oriented applications live and die by their ability to read and write relational database tables. In the previous two chapters you’ve seen how easy it is to manage persistent entities in a Seam application, thanks in large part to the ability of JPA and Hibernate to transparently map objects to relational database tables and to move them back and forth through use of a persistence manager. These object-relational mapping (ORM) frameworks do away with verbose JDBC/SQL code sprinkled throughout the objects in the data access layer (or worse, directly in the view).

Despite the convenience of using an ORM tool over straight JDBC, you must still perform repetitive tasks. The general belief is that ORM operations need to be wrapped in a data access object (DAO) to eliminate boilerplate code and remove code duplication across projects. There’s a number of DAO frameworks that take care of this tedious work either by generating code or by providing template classes to isolate data operations and exceptions from the business logic. Here’s a small sampling of these frameworks:

DAO frameworks such as these are capable of handling the following tasks:

  • Create or obtain a reference to the persistence manager
  • Manage CRUD operations and transactions with parameterized template classes
  • Reduce casting by using generic types or generated code
  • Provide a facility to define queries declaratively and help manage the result set

There’s nothing stopping you from using a data access layer in a Seam application. In fact, for large-scale projects, I might be inclined to encourage such a design. But Seam dispels the myth that the persistence manager must be trapped inside a DAO. Instead, Seam proposes that the persistence manager is the DAO. But the persistence manager isn’t a page controller—that’s where your Seam component fits in. It negotiates with the persistence manager to perform data access operations. Not only does this design collapse layers, it also introduces a stateful component. The data access layer has become so ingrained in our minds that it seems we can’t just let it go and try to justify its existence by convincing ourselves that it protects us from technology change (the same goes for the service layer).

While Seam can manage the persistence context and allows it to be easily shared among components, which you learned about in the previous chapter, the Seam developers recognized that you still need to write a page controller. The Seam Application Framework is a variation on the generic DAO framework, except it gets the persistence manager and the page controller to work as a single unit rather than introducing an additional layer of stacking. In addition, the classes in the Seam Application Framework are aware of the state of the entities being exchanged (making it stateful), rather than just blindly passing on operations to the persistence manager. The framework includes two categories of persistence controllers, which you’ll learn about in the next section: one that manages a single entity and one that manages a result set. These controllers cover the following tasks:

  • Act as a JSF form bean and action bean (page controller)
  • Create prototype instances, either to be persisted or used as part of a query
  • Assist in managing the state of the entity instance or query result set
  • Retrieve the entity instance or query result set on demand
  • Monitor the parameters to determine when the managed data needs to be refreshed
  • Use Java 5 generics to provide type-safe checking and eliminate casting for operations performed on the entity instance or query result set
  • Define transactional boundaries around persistence operations
  • Prepare status messages and raise events when the transaction wrapping the operation commits successfully

Thus, the classes that comprise the Seam Application Framework don’t purposelessly wrap the persistence API. Instead, they foster rapid development of database-oriented applications by handling many of the auxiliary concerns required to manage persistent entities. Let’s take a look at what classes are available and how they interact with the persistence manager.

10.1.2. The persistence controllers

The Seam Application Framework is a hierarchical set of classes, all extending from the Controller base class. Controller contains convenience methods for accessing the Seam contexts and component instances, interacting with the Servlet API and JSF life cycle, logging messages, registering JSF messages, and raising events. The classes in this hierarchy make extensive use of Java 5 generics to provide strong typing.

These classes are intended to be used as JavaBean components. If you wanted to extend one to create an EJB component, you’d need to define an interface for the class since one is not provided. Even then, the PersistenceController, which is the primary descendent in the class hierarchy, is designed to be used with a Seam-managed persistence manager. In particular, it provides generic access—of the Java 5 variety—to the persistence manager as part of its class definition, shown here:

   public abstract class PersistenceController<T> extends Controller { ... }

In this declaration, the generic type parameter T is a placeholder for the persistence manager. Extending from the PersistenceController are three branches of classes that facilitate interaction with the persistence manager. Each branch has an implementation for JPA and one for Hibernate. For JPA, T is replaced with EntityManager and for Hibernate it is replaced with Session. The controller classes in the Seam Application Framework are summarized in table 10.1. In each of these classes, the generic type parameter E is a placeholder for the entity class.[1]

1 According to http://java.sun.com/docs/books/tutorial/java/generics/gentypes.html, T is the common parameter name for a Type. E is common for Element, though here you can think of it as Entity.

Table 10.1. The three branches of parent classes in the Seam Application Framework

Type/Purpose

JPA

Hibernate

Home<T, E> Manages a single entity instance and supports CRUD operations. EntityHome<E> HibernateEntityHome<E>
Query<T, E> Manages a JPQL/HQL query result set. Supports restrictions, ordering, and pagination. EntityQuery<E> HibernateEntityQuery<E>
PersistenceController Parent class for developing JSF page controllers. Has convenience methods for interacting with the persistence manager, Seam, and JSF. EntityController HibernateEntityController

The JPA implementation of this class hierarchy is diagrammed in figure 10.1.

Figure 10.1. The Seam Application Framework hierarchy for JPA. There’s an equivalent one for Hibernate.

So what’s the benefit of wrapping the persistence manager? There are in fact two: transaction boundaries and generics. Although the persistence manager can control or participate in a transaction, it can’t dictate transaction boundaries. The methods on the persistence controller, on the other hand, are decorated with the @Transactional annotation to guarantee that the persistence operations are nestled within an explicit transaction. In the previous chapter, you learned that Seam uses global transactions around a JSF request, so this is only a concern outside that environment. Generics allow these classes to be form-fitted to a persistence manager implementation and an entity class. Let’s focus on the benefits of generics.

A generic approach

It’s no secret that Seam embraces the language extensions introduced in Java 5 in order to simplify the Java EE programming model. You’ve already seen that Seam makes extensive use of annotations as a means of providing declarative behavior. Seam builds on this adoption of Java 5 by using generics throughout the Seam Application Framework. Generics help cut down on casting,[2] thus removing a lot of unnecessary clutter from your code. For example, you can adopt the EntityHome to the Round entity using a parameterized type:

2 For a more detailed discussion of generics and how they help eliminate casting, please refer to the technical article on generics at http://java.sun.com/developer/technicalArticles/J2SE/generics/.

  EntityHome<Round> roundHome = new EntityHome<Round>();

You can then initialize a new instance of the entity without the need for a cast:

  Course newRound = roundHome.newInstance();

The generics support is only relevant if you extend the framework classes in Java. You also have the option of declaring your framework components entirely in XML. In that case, the generic type parameters don’t play a role since the instances are only referenced using the dynamically typed (or “duck-typed”) EL. Let’s consider when each is the appropriate choice.

10.1.3. Two ways to play

The classes in the Seam Application Framework aren’t components themselves—meaning they don’t bear the @Name annotation. They are component templates, akin to the Seam-managed persistence classes covered in the previous chapter. As you learned, one way to commission a template class as a Seam component is by declaring and configuring it in the component descriptor, as shown here for the roundHome component, which you’ll encounter later in this chapter:

  <framework:entity-home name="roundHome"
    entity-class="org.open18.model.Round"/>

This component declaration falls under the built-in component namespace http://jboss.com/products/seam/framework, prefixed as framework throughout this chapter. Like the persistence framework classes, the application framework classes are designed to be controlled entirely through configuration. The XML-based approach is amazingly flexible, in large part due to the capabilities of the Seam component model and the ubiquitous EL. When defined in this way, the component is fully capable of preparing an entity instance to bind to the JSF form inputs and handling the form’s actions. All of the necessary functionality is built right into the Home class.

If the feature you’re looking for isn’t already covered by the framework class, the configuration-only approach is going to come up short. Fortunately, these classes are designed to be extended. You can extend them in Java or Groovy to build your own custom components. This option gives you the most flexibility for all the reasons class extension is useful. As an added bonus, you can fulfill the generic type parameters on the parent class to get type-safe checking. Here’s the same roundHome component defined in Java:

  @Name("roundHome")
  public class RoundHome extends EntityHome<Round> { ... }

Don’t feel that you have to make the decision between Java or XML up front. From the standpoint of the rest of the application, the end result is the same, a Seam component. You can comfortably mix the two styles in your application, or even on the same component. In fact, the most flexible approach is to define properties in XML and the methods on a subclass, referencing the subclass in the component descriptor. This flexibility is the essence of the unified component model in Seam. You’re now ready to swing away by implementing CRUD screens to manage instances of the Round entity using a Home component.

10.2. Stateful CRUD using the Home component

Managed persistence has a history of being at odds with sound object-oriented design. EJB 2 championed the use of distributed objects. As a consequence, developers quickly resorted to using data transfer objects (DTOs) to push aggregated data over the wire as a way to reduce network traffic. These domain objects were void of behavior. Unfortunately, the intelligence in domain objects didn’t get restored once “lightweight” DAO frameworks replaced EJB 2 because the same flawed architecture was adopted. Treating domain objects as buckets for holding data, without giving them real behavior, is an anti-pattern known as the Anemic Domain Model.[3] As is the fate of all things out of balance, a change was imminent.

3 Martin Fowler first presented the concept of an Anemic Domain Model on his bliki: http://martinfowler.com/bliki/AnemicDomainModel.html.

10.2.1. Remedying the Anemic Domain Model

In an attempt to swing the pendulum away from the Anemic Domain Model, as illustrated in figure 10.2, some contemporary frameworks, such as Ruby on Rails, promote the use of the Active Record design pattern as a way of making domain objects active participants in an object-oriented design. In the Active Record pattern, the domain object is mapped directly to a database record, just like with ORM. But this relationship is pushed further by allowing the domain object to save and retrieve itself from the database. Thus, in addition to encapsulating data, it encapsulates data access logic.

Figure 10.2. An industry trend toward adding more behavior to domain model objects. The Anemic Domain Model only holds state, whereas the Active Record pattern encapsulates state and performs data access logic.

Seam, being a progressive framework itself, supports the Active Record pattern, right? Absolutely not. For one, the Active Record pattern has been attempted once in J2EE and failed miserably. Entity beans from the EJB 2 era—not to be confused with JPA entity classes in EJB 3—bear a close resemblance to the Active Record pattern since they internalize data access logic. This interaction is particularly evident in bean-managed persistence (BMP) entity beans, which embed SQL statements directly in the life-cycle methods. Just like domain objects in the Active Record pattern, EJB 2 entity beans can create, update, load, and remove the database records to which they map.

One of the main factors in the failure of entity beans is the hard link between the domain object and the persistence framework. There is no separation of concerns. To put it simply, entity beans are not POJOs. In fact, it’s difficult to implement the Active Record pattern using POJOs. Hopefully, by now, we are in agreement that POJOs are indicators of a sound design. They are easy to test and they are reusable. The main reason Seam doesn’t support the Active Record pattern is because it has a better solution based on POJOs that strikes a nice balance between the fading Anemic Domain Model and the overeager Active Record pattern.

10.2.2. Giving the domain object a Home

Entity classes are POJOs. This trait is good for reasons just mentioned, but limiting because they can’t set transaction boundaries and can’t manage their own persistent state (1 for POJOs, 0 for active domain models). But history has taught us that entity classes shouldn’t handle this work anyway since it’s not a good separation of concerns. Ideally, we want a solution that provides a competent domain model without introducing tight coupling between the domain object and the persistence framework.

Introducing the Home component

Seam answers this challenge by introducing the Home component (herein referred to as a Home). A Home manages an entity instance by caching it and coordinating CRUD operations on it with the persistence manager, all completely transparent to the entity instance. Each Home is represented by the Home class, which extends from PersistenceController, or any subclass of Home. As I’ve alluded to throughout this chapter, the framework classes, such as Home, are abstract classes that are, in reality, just component templates. They are also agnostic of the persistence framework. Seam provides two implementations of Home: one for JPA, EntityHome, and one for Hibernate, HibernateEntityHome. The class diagram for EntityHome is shown in figure 10.3. This section teaches you how to use the Home component, focusing on the JPA implementation.

Figure 10.3. The class diagram of EntityHome. Several supplemental operations have been excluded.

A Home is a keeper of an entity instance, hence the term (in case you were wondering). You can use the following analogy to give more meaning to the term. The entity class is like a family, and an instance of that class is a member of the family. The Home, illustrated in figure 10.4, is where you go to find a family member. The only restriction is that at any given time, a Home can only accommodate a single family member. (The whole house to yourself! Wouldn’t that be nice?) The entity instance is said to be the Home’s context.

Figure 10.4. A Home manages an entity instance, negotiating with the persistence manager to retrieve it.

You use a Home to manage an existing record by supplying an identifier value to the setId() method on Home. The identifier value represents a unique entity instance and is mapped to the database table through the @Id property on the entity class. The getInstance() method on Home uses this identifier to look up the entity by calling the find() method on the persistence manager. Figure 10.5 shows the lookup process.

Figure 10.5. Sequence diagram showing how the Home object resolves an entity instance.

When a different identifier is assigned to a Home, the ensuing call to getInstance() performs a fresh lookup to retrieve the entity instance associated with that identifier. If an identifier isn’t assigned when getInstance() is called, a new entity instance is created internally by a call to the createInstance() method. Another method, isManaged(), reports whether the entity instance maintained is transient or persistent. The three operations that change the entity instance managed by the Home are listed in table 10.2.

Table 10.2. The methods on Home that are used to set the instance that is managed

Method

Description

setId() Assign the id. Supplied to the persistence manager to retrieve the instance.
setInstance() Manually establish the instance, bypassing the lookup by id mechanism.
clearInstance() Forcefully clear both the id and the instance.

The Home serves as the main interface of the domain model object in this design, which is now an aggregate of classes rather than a single “active” class like with Active Record.

A domain model coalition

A Home encapsulates the entity instance and the persistence manager, facilitating communication between them without making them aware of each other, as shown in figure 10.6. To the outside world, this domain object appears as a single unit, allowing the domain model to be “active” without contaminating the entity class with data access logic.

Figure 10.6. A Home acts as a façade for the domain object, delegates operations to the entity instance and the persistence manager, and facilitates communication between them.

This design most closely resembles the Mediator Design pattern. In the words of the Gang of Four (Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley Professional, 1994):

A mediator serves as an intermediary that keeps objects in a group from referring to each other explicitly.

A Home uses the persistence manager to manipulate the persistence state of the entity instance it manages, seeing it through its entire life cycle, as shown in figure 8.3 in chapter 8. To coordinate these state transitions, the Home performs the well-known CRUD operations by delegating the work to the persistence manager. This encapsulation and delegation is what makes the Home pattern a good object-oriented design. These façade methods on the Home class are listed in table 10.3.

Table 10.3. The methods on Home dedicated to interacting with the persistence manager

Method

Description

getInstance() Retrieves the instance managed by this Home object
isManaged() Reports whether the instance is in a transient or persistent state
persist() Saves the transient instance to the database
update() Synchronizes the persistent instance with the database
remove() Makes the instance transient by removing it from the database

What the Mediator pattern brings to the table, and how it sets the Home apart from the entity instance itself, is that the Home is both transactional and stateful.

Controlling transactions and state

The Home class provides transactional boundaries around the methods on the persistence manager, declared using Seam’s @Transactional annotation. An excerpt of the persist() method on EntityHome is shown here:

  @Transactional
  public String persist() {
      getEntityManager().persist(getInstance());
      getEntityManager().flush();
      ...
      return "persisted";
  }

In this method, you can see how the Home is mediating between the entity instance and the persistence manager. But the Home’s work extends far beyond the boundaries of a transaction. By default, an instance of Home is scoped to the conversation context, allowing it to maintain the entity instance throughout the use case. When combined with an extended persistence context, that translates into not having to retrieve the entity instance on each page transition or having to merge it when changes need to be sent to the database. Instead, the persistence manager can be responsible for tracking the changes in the entity instance. That brings us to the update operation.

Assume that you retrieved an entity instance using a Home and overlaid it on a form in a web page. You would bind the action of the form to the update() method on Home to have those changes pushed to the database when the form is submitted. An excerpt of the update() method on the EntityHome is shown here:

  @Transactional
  public String update() {
      joinTransaction();
      getEntityManager().flush();
      ...
      return "updated";
   }

As you can see, the update() method performs two operations on the persistence manager, neither of which issues an explicit update. It ensures the persistence manager is enlisted in the active transaction, which is only applicable if Seam is configured to use JTA transactions, and then flushes the persistence context to synchronize changes to the database. The changes become permanent when the transaction commits.

 

Note

The update() method doesn’t need to perform a merge operation, even if the entity instance isn’t stored in a long-running conversation. The reason is because the entity instance is fetched from the database in the Update Model Values phase prior to the values from the form being applied to it. Thus, the instance is guaranteed to be managed when the update() method is called. Any changes made during the Update Model Values phase are detected and synchronized to the database.

 

This transparent management of the entity instance is the quintessence of stateful behavior and is arguably more “active” than what the Active Record pattern affords you. It’s certainly a more sound object-oriented design than EJB 2 entity beans with data transfer objects. With the transactional boundaries established at the fringes of the methods on the Home class, you are free to give your entity classes behavior, even if it necessitates a transaction. However, logic that requires direct interaction with the persistence manager is better placed on the Home object itself.

That’s enough practical discussion about the Home component. Let’s put it to work by using it to develop a data-entry screen for a round of golf in the Open 18 application. We will task it to see if it lives up to its design goals.

10.2.3. Putting Home to work

After taking a leisurely stroll through 18 holes on the golf course—leaving plenty of work for the groundskeepers in your wake—you’ll want to record your golf score to track your improvement—or lack thereof. Eventually, this data can be used to calculate a golf handicap value. Let’s put together logic to manage this data by following the same design used in a seam-gen project. We need to create the entity class, a Home object to manage it, and the JSF templates for rendering the view and edit screens. Mimicking the layout that seam-gen uses, the edit screen should appear like figure 10.7. The view screen only differs in that the data is displayed as read-only.

Figure 10.7. The form for adding a new round, the final product of this section’s tutorial

A golf round is represented by the Round entity class, shown in listing 10.1, which establishes the O/R mapping to the ROUND table where the data is persisted. Note that an entity class must implement Serializable if it’s to be stored in a stateful context (e.g., conversation) or passed through a remote interface.

Listing 10.1. The Round entity class

Note the @ManyToOne mappings to Golfer and TeeSet. The fetch type declared in this annotation is the global fetch strategy, which in this case is lazy. Later on, you’ll learn how to temporarily change the fetch strategy to eager for a single JPQL/HQL query to optimize the data retrieval. As you’ve learned, though, it’s perfectly safe to cross lazy associations in the view thanks to Seam’s proper scoping of the persistence context.

A round is associated with a tee set—the area on the golf course where you begin each hole. (A tee set isn’t a collection, but rather a singular entity that represents the position and color of the tee box on each hole.) The relationship between a round and a golf course is implied indirectly through the tee set relationship. A round is also associated with a golfer, which we assume is the golfer using the application when a new round is being entered. You will see how a Home can be used to establish these two relationships. Let’s start by looking at the declaration of the Home object that manages the Round entity.

Declaring a Home

The Home implementation class is typically quite sparse. The purpose of the implementation class is to fulfill the generic type parameter for the Home type and yield a Seam component. It can also provide custom routines that the template class doesn’t cover. The following class definition shows a bare-minimum implementation subclass of Home:

  package org.open18.action;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.framework.EntityHome;
  import org.open18.model.Round;

  @Name("roundHome")
  public class RoundHome extends EntityHome<Round> {}

Believe it or not, this declaration is capable of performing CRUD for a Round entity! All of the necessary logic is inherited. The Round entity class is passed as a generic type parameter in the extends clause to make the EntityHome class aware of the type of entity it is managing. The @Name annotation makes it a Seam component. The scope isn’t declared, so it defaults to the conversation context, inherited from the superclass.

By extending EntityHome, we are implicitly selecting JPA as the persistence framework to manage the Round entity. EntityHome feeds EntityManager to the generic type parameter T in Home<T, E>:

  public class EntityHome<E> extends Home<EntityManager, E> { ... }

If you’re using Hibernate natively, define the RoundHome component by extending the HibernateEntityHome class instead:

  @Name("roundHome")
  public class RoundHome extends HibernateEntityHome<Round> { ... }

The Hibernate implementation of the Home superclass has exactly the same methods as the JPA implementation, so switching between the two has a low impact on the application.

Retrieving the persistence manager

As with all the persistence controllers in the Seam Application Framework, the persistence manager is retrieved by the method getPersistenceContext(). In the JPA implementation, this method looks for a Seam-managed persistence context named entityManager and in the Hibernate implementation, session (or, hibernate-Session as of Seam 2.1). If you aren’t able to adhere to this naming convention, you have three options to customize it:

  • Override the method getPersistenceContextName() method and supply an alternative component name for Seam to use:
    public String getPersistenceContextName() { return "em"; }
  • Override the getPersistenceContext() method and look up the persistence manager explicitly:
    public EntityManager getPersistenceContext() {
    return (EntityManager) Component.getInstance("em");
    }
  • Use component configuration to wire in a persistence manager:
    <framework:entity-home ... entity-manager="#{em}"/>

You can also override the getEntityManager() method if you’re using JPA or getSession() if you’re using Hibernate. These methods are the delegates of the getPersistenceContext() method in the respective class hierarchy.

Taking the XML road

As mentioned in section 10.1.3, you have the option of defining the Home component in the component descriptor using XML. Since generic types are not available in this case, you have to specify the entity class explicitly:

  <framework:entity-home name="roundHome"
    entity-class="org.open18.model.Round"/>

The equivalent XML element for Hibernate is <framework:hibernate-entity-home>. Using XML works for simple cases, but as you’ll soon discover, there’s more value in extending the Home class in Java (or Groovy) to create a more intelligent domain model. As another option, you can use XML to configure a Java component that is a subclass of Home.

Binding a Home to a form

The Home component plays a dual role in a JSF form. It exposes the entity instance, whose properties are bound directly to the inputs in the form. It also responds to the form actions. This direct binding cuts out the “middleman” so you can get right down to business. The JSF form used in the RoundEdit.xhtml template is shown in listing 10.2. We analyze this form in the remainder of this section. Note that the <s:decorate> tags simplify the markup for each field, as described in chapter 3. The possible enum constants for the weather select menu are retrieved by a factory named weatherCategories, which isn’t shown here.

Listing 10.2. The editor form for a golf round
  <h:form id="roundForm">
    <rich:panel>
      <f:facet name="header">
        #{roundHome.managed ? 'Edit' : 'Add'} Round
      </f:facet>
      <s:decorate id="dateField" template="layout/edit.xhtml">
        <ui:define name="label">Date:</ui:define>
        <rich:calendar id="date" datePattern="MM/dd/yyyy"
          value="#{round.date}"/>
      </s:decorate>
      <s:decorate id="notesField" template="layout/edit.xhtml">
        <ui:define name="label">Notes:</ui:define>
        <h:inputTextarea id="notes" cols="80" rows="3"
          value="#{round.notes}"/>
      </s:decorate>
      <s:decorate id="totalScoreField" template="layout/edit.xhtml">
        <ui:define name="label">Total score:</ui:define>
        <h:inputText id="totalScore" value="#{round.totalScore}"/>
      </s:decorate>
      <s:decorate id="weatherField" template="layout/edit.xhtml">
        <ui:define name="label">Weather:</ui:define>
        <h:selectOneMenu id="weather" value="#{round.weather}">
          <s:selectItems var="_weather" value="#{weatherCategories}"
          label="#{_weather.label}" noSelectionLabel="-- Select --"/>
          <s:convertEnum/>
        </h:selectOneMenu>
      </s:decorate>
      <div style="clear: both;">
        <span class="required">*</span> required fields
      </div>
    </rich:panel>
    <div class="actionButtons">
      <h:commandButton id="save" value="Save"
         action="#{roundHome.persist}"
         rendered="#{!roundHome.managed}" disabled="#{!roundHome.wired}"/>
      <h:commandButton id="update" value="Update"
         action="#{roundHome.update}"
         rendered="#{roundHome.managed}"/>
      <h:commandButton id="delete" value="Delete"
         action="#{roundHome.delete}"
         rendered="#{roundHome.managed}"/>
      <s:button id="discard" value="Discard changes" propagation="end"
        view="/Round.xhtml"
        rendered="#{roundHome.managed}"/>
      <s:button id="cancel" value="Cancel" propagation="end"
        view="/#{empty roundFrom ? 'RoundList' : roundFrom}.xhtml"
        rendered="#{!roundHome.managed}"/>
    </div>
  </h:form>

The input elements are bound to an instance of Round managed by RoundHome through the value expression root #{roundHome.instance}. Since the #{roundHome.instance} is used so frequently in this template, it makes sense to create an alias for it. As you learned in chapter 6, aliases are defined using a factory component. The factory can be defined in Java using the @Factory annotation on a method or as a <factory> element in the component descriptor. Let’s define it in XML:

  <factory name="round" value="#{roundHome.instance}"/>

You can now use the expression root #{round} in place of #{roundHome.instance} throughout the template. Not only does it save a couple of keystrokes, it also hides the implementation details of the Home pattern, making it appear as though you are working with the entity itself! When choosing an alias, make sure the name isn’t already in use or doesn’t conflict with the name of an existing component. Otherwise, you may be surprised when you don’t get the object you’re expecting.

Two questions are raised by the form in listing 10.2 that are integral in understanding how the Home object works:

  • When is the instance referenced by #{roundHome.instance} initialized?
  • What is the purpose of #{roundHome.managed}?

Let’s answer each in turn.

Initializing an instance from a prototype

The RoundHome component is instantiated the first time its component name is referenced on the RoundEdit.xhtml page. Most references are to the method getInstance(), the most frequently used method on Home. It serves the underlying entity instance, whether it be transient—not yet saved—or persistent.

As mentioned earlier, when the getInstance() method is first called, it either looks up an existing entity instance using the find() method on the persistence manager or creates a new transient instance, depending on whether an identifier has been established on the Home, a determination which is made by a call to the isIdDefined() method. Since we don’t yet have any rounds to manage, we focus on creating a new instance.

One of the Home’s functions is to create an entity instance from a prototype. It can do more than just instantiate the entity class, though that’s the default case. The getInstance() method delegates to the createInstance() method when a new instance needs to be created. The Home figures out which entity class to instantiate in one of two ways. If the Home was defined through class extension, and an entity class was passed as a parameterized type, the Home uses reflection to determine the type. In this case, the createInstance() method instructs the entity class to create a new instance of itself:

  protected E createInstance() {
      ...
      return getEntityClass().newInstance();
  }

You can override the createInstance() method to provide a more sophisticated prototype. Since the round must be associated with the current golfer, this method provides the perfect opportunity to establish this relationship.

Let’s assume that the current user is a golfer and the corresponding Golfer instance is stored under the context variable currentGolfer. The initialization of this variable is part of the authentication routine implemented in the next chapter. To get by with the stub authentication prepared by seam-gen, you can use the following XML-based configuration:

  <framework:entity-query name="currentGolferQuery"
    ejbql="select g from Golfer g where g.username = #{identity.username}"/>
  <factory name="currentGolfer" scope="conversation" auto-create="true"
    value="#{identity.loggedIn ? currentGolferQuery.singleResult : null}" />

The currentGolfer is injected into RoundHome using @In and wired to the Round instance in the overridden createInstance() method. The injection point shown here is marked optional to allow the Home to be used in the absence of an authenticated golfer:

  @In(required = false)
  Golfer currentGolfer;

  @Override
  protected Round createInstance() {
      Round round = super.createInstance();
      round.setGolfer(currentGolfer);
      round.setDate(new java.sql.Date(System.currentTimeMillis()));
      return round;
  }

If you’re using the XML-based approach, you instead supply a prototype instance as a value expression to the new-instance attribute of <framework:entity-home> (or <framework:hibernate-entity-home>). To support declaring a Home component in XML, we create a prototype named roundPrototype, wire the authenticated golfer instance and built-in current date component to it, and then assign it to the new-instance property of the RoundHome component in the XML definition:

  <component name="roundPrototype" class="org.open18.model.Round"
    scope="stateless">
    <property name="golfer">#{currentGolfer}</property>
    <property name="date">#{currentDate}</property>
  </component>

  <framework:entity-home name="roundHome" class="org.open18.action.RoundHome"
    new-instance="#{roundPrototype}"/>

You can also use the XML definition to configure the newInstance property of a Home component defined in Java. Note that the #{roundPrototype} value expression isn’t resolved when assigned to the newInstance property. That’s because newInstance property is of type ValueExpression. The expression is resolved in the createInstance() method instead of calling getEntityClass().newInstance().

What’s the status?

The entity instance held by the Home object can be in either a persistent or a transient state. The contains() method on the persistence manager is used to check the state of an entity instance. The isManaged() method on EntityHome performs this check, shown here:

  @Transactional
  public boolean isManaged() {
      return getEntityManager().contains(getInstance());
  }

There is an equivalent method on HibernateEntityHome. Looking back at listing 10.2, you’ll notice that different buttons are rendered based on the status returned by this method. The editor only permits the user to persist the round if the instance isn’t currently managed by the persistence context. Conversely, the user can only delete or update the instance if it’s managed by the persistence context. This UI restriction doesn’t avert an attempt to enter a duplicate record. You still need to implement that logic. It just makes the form cognizant of whether it’s being used to create or update a record.

Let’s focus on the use case of saving a new round. When the user clicks the Save button, the #{roundHome.persist} action method is activated and the create operation (the C in CRUD) is delegated to the persist() method of EntityManager. However, we can’t save an instance of a round until the association to a tee set is satisfied. Let’s consider strategies to allow the user to make this selection.

Wiring in a tee set

One logical place to start when adding a new round is the detail page of the tee set played, rendered by the TeeSet.xhtml template. At the bottom of the page, a button can be added that lets the user enter a new round. The button will pass the identifier of the tee set using the teeSetId request parameter. The return page is also maintained using the roundFrom request parameter in the event the user decides to cancel. The button is defined as follows:

  <s:button value="Add round" view="/RoundEdit.xhtml">
    <f:param name="teeSetId" value="#{teeSetHome.instance.id}"/>
    <f:param name="roundFrom" value="TeeSet"/>
  </s:button>

The TeeSetHome component, named teeSetHome and created in chapter 2 by seam-gen, manages a TeeSet instance in the same way that RoundHome manages a Round instance. Assuming the tee set has an identifier of 10, the following URL is created by this button:

  http://localhost:8080/open18/RoundEdit.seam?teeSetId=10&roundFrom=TeeSet

On the RoundEdit.xhtml page, TeeSetHome can once again be used to retrieve the tee set instance using the value of the teeSetId parameter. The TeeSet is then wired into the new Round instance. The identifier of the tee set is established on TeeSetHome using a page parameter, defined in the RoundEdit.page.xml page descriptor, which is adjacent to RoundEdit.xhtml. The other page parameter copies the value of the roundFrom request parameter to the page-scoped roundFrom variable:

  <page>
    <param name="teeSetId" value="#{teeSetHome.teeSetId}"/>
    <param name="roundFrom"/>
  </page>

The setTeeSetId() and getTeeSetId() methods on TeeSetHome delegate to setId() and getId() on the superclass, performing a conversion to Long, the identifier’s type.

The next step is to wire the TeeSet instance managed by TeeSetHome to the Round instance. This logic is handled in the wire() method on RoundHome. The TeeSetHome component must be injected into RoundHome so we can access the managed tee set:

  @In(create = true)
  private TeeSetHome teeSetHome;

  public void wire() {
      TeeSet teeSet = teeSetHome.getDefinedInstance();
      if (teeSet != null) {
          getInstance().setTeeSet(teeSet);
      }
  }

The method getDefinedInstance() on TeeSetHome is a custom method that strictly attempts to retrieve a TeeSet instance from the persistence manager if the id is established, as determined by the isIdDefined() method.

The final step is to call the wire() method from a page action defined in RoundEdit.page.xml so that the association is established prior to the editor being rendered:

  <page>
    <param name="teeSetId" value="#{teeSetHome.teeSetId}"/>
    <param name="roundFrom"/>
    <action execute="#{roundHome.wire}"/>
  </page>

To prevent users from clicking the Save button if they haven’t yet selected a tee set, we can add the convenience method isWired() to RoundHome that is used to toggle the disabled attribute of the Save button:

  public boolean isWired() {
      if (getInstance().getTeeSet() == null) {
          return false;
      }
      return true;
  }

The page fragment in listing 10.3 renders the tee set associated with the round below the editor form. Information about the tee set is read from the teeSet property on Round.

Listing 10.3. The panel showing the tee set associated with the round
    <rich:tabPanel>
      <rich:tab label="Tee Set">
        <div class="association">

        <h:outputText value="Tee set not selected"
          rendered="#{round.teeSet == null}"/>
        <rich:dataTable var="_teeSet" value="#{round.teeSet}"
          rendered="#{round.teeSet != null}">
          <h:column>
            <f:facet name="header">Course</f:facet>
            #{_teeSet.course.name}
          </h:column>
          <h:column>
            <f:facet name="header">Color</f:facet>
            <div title="#{_teeSet.color}" class="colorSwatch"
              style="background-color: #{_teeSet.color}"/>
          </h:column>
          ...
          <h:column>
            <f:facet name="header">Position</f:facet>
            #{_teeSet.position}
          </h:column>
        </rich:dataTable>
      </div>
    </rich:tab>
  </rich:tabPanel>

Everything’s in place for a new round to be persisted! Although there was a lot of explaining, the work boiled down to creating a Home implementation class, a page parameter, a page action, and a JSF template. The rest of the work is handled by JSF and the Home component working together. JSF binds the values from the form to the properties on the Round entity instance, and the Home component delegates to the persistence manager to save the record.

Now that a round is stored in the table (assuming you were brave enough to click the Save button), we can move on to the other letters in the CRUD acronym. Let’s tackle R by creating a page to display the round just entered.

Pulling up a round

The template Round.xhtml, shown in listing 10.4, handles the task of displaying the details of a round. It’s identical to the RoundEdit.xhtml template except that the input fields are replaced with read-only output.

Listing 10.4. The panel that renders the details of a round

To render the data of an existing round, the getInstance() method needs to return the record in the database. That means the identifier of the round must be assigned to the RoundHome component prior to the getInstance() method being called. This assignment is a good fit for a page parameter. However, the id property on Home is of type java.lang.Object, which doesn’t provide JSF with enough information to convert the incoming parameter to the identifier’s type, which is java.lang.Long. We could create a typed “getter” method that passes the converted value to setId(). But a better solution is to leverage the converter feature of page parameters. The javax.faces.Long converter is registered on the page parameter for roundId in the Round.page.xml descriptor to instruct JSF to convert the string-based request parameter to a java.lang.Long:

  <param name="roundId" value="#{roundHome.id}"
    converterId="javax.faces.Long"/>

That’s all there is to it! When the Round.xhtml template is requested, the corresponding entity instance is loaded from the database according to the request parameter value.

As an alternative to using a page parameter to assign the identifier request parameter to Home, you can inject the parameter into the component directly by using the @RequestParameter annotation. In the case of RoundHome, you create a setRoundId() method to receive the injected request parameter and then set the identifier on the superclass. JSF converts the value to the method parameter’s type:

  @RequestParameter
  public void setRoundId(Long id) {
      super.setId(id);
  }

The option of using the @RequestParameter annotation or the XML-based page parameter is up to you. Keep in mind, though, that page parameters also outject values back to the query string in addition to injecting request parameter values. Let’s consider how the presence of the page parameter affects the Edit button:

  <div class="actionButtons">
    <s:button id="edit" view="/RoundEdit.xhtml" value="Edit"/>
  </div>

Notice that there are no parameters nested in the button component. Thanks to page parameters, the roundId is automatically added to the generated URL, saving you the trouble of having to use <f:param> to include it explicitly. When thinking about how the URL is built, keep in mind that the page parameters are read from the page descriptor corresponding to the target view ID, which in this case is RoundEdit.xhtml. Thus, the page parameters are read from the RoundEdit.page.xml descriptor. That means the roundId page parameter must be added there as well.

  <page>
    <param name="roundId" value="#{roundHome.id}"/>
    <param name="roundFrom"/>
    <param name="teeSetId" value="#{teeSetHome.teeSetId}"/>
    <action execute="#{roundHome.wire}"/>
  </page>

Before we move on, there’s one last thing to consider. Recall that the getInstance() method retrieves the entity instance using the persistence manager’s finder method:

  getEntityManager().find(getEntityClass(), getId());

This default lookup is naïve because it leaves all lazy associations uninitialized. Although the conversation-scoped persistence context makes worrying about loading lazy associations a thing of the past, it’s a good idea to eagerly fetch data when you know you’ll be traversing associations or collections in the view. Eager fetching is paramount to preventing the n + 1 select problem.

 

Tip

When using Hibernate, you can detect n + 1 select problems by observing the log output or by producing a report from the return value of the get-Statistics() method on SessionFactory. These two features require the properties hibernate.show_sql and hibernate.generate_statistics to be enabled in Hibernate, respectively.

 

On the round detail page, we know we need to render the tee set, course, and golfer, so we might as well initialize these associations in the finder query. This is done by temporarily promoting the fetching strategy from lazy to eager using the join fetch JPQL operator. You customize the loading behavior by overriding the loadInstance() method on EntityHome:

  protected Round loadInstance() {
      return (Round) getEntityManager.createQuery(
          "select r from Round r " +
          "join fetch r.golfer g " +
          "join fetch r.teeSet ts " +
          "join fetch ts.course c " +
          "where r.id = :id")
          .setParameter("id", getId())
          .getSingleResult();
  }

Consult a JPA reference for more information on the join fetch operator. The loadInstance() method can also be used to postprocess the instance after it’s loaded. For instance, if you store XML in one of the columns and need to unmarshal that data to a Java structure, you can take this opportunity to perform such logic.

With the detail page out of the way, things start to get interesting in the case of editing an existing entity. That brings us to the U and D in CRUD, which we cover in the next section.

10.2.4. Venturing away from home

A Home can only maintain state for as long as it lives. To stretch out the lifetime of a Home, which is scoped to the conversation by default, you need to activate a long-running conversation. This ensures that the RoundHome component, the entity instance, and the persistence manager remain in scope while the user is working on modifying the round, even if it means departing the editor screen to go find a tee set.

Switching to a long-running conversation

As you learned in chapter 7, there are many ways to begin a long-running conversation. Since we already have the RoundEdit.page.xml descriptor setting up the entity instance, we might as well keep it busy by having it begin the long-running conversation as well:

  <page>
    <begin-conversation join="true"/>
    <param name="roundId" value="#{roundHome.id}"/>
    <param name="roundFrom"/>
    <param name="teeSetId" value="#{teeSetHome.teeSetId}"/>
    <action execute="#{roundHome.wire}"/>
  </page>

Depending on how the round editor ties into the application, you may decide that nesting the conversation is more appropriate. With the long-running conversation in place, the Round instance being edited remains managed by both the Home and the persistence context between the time the editor is first rendered to when the user submits the form successfully. You can tell that the Round instance is managed (it’s in the persistent entity state) because the buttons that call the update() and remove() action methods on Home are displayed below the form.

But keeping the entity managed involves more than just showing the right controls. As you learned in the previous two chapters, using an extended persistence context yields important benefits. First, the database only has to be asked once for a record during the use case since the application does its part to “remember” what the database retrieved for it. The other benefit is that the update statement only executes if the entity instance changes. If the entity instance hasn’t changed, there’s nothing to ask the database to do.

Off to find a tee set

With a long-running conversation active, the user is not stuck on the editor screen. You can allow users to roam freely throughout the application, as long as the conversation token is restored when they return. In the case of the round editor, this allows the user to traverse to the tee set listing page, search for, and select a new tee set to link to the round. This step is necessary when the user wants to change the tee set for the round being edited or when a tee set identifier wasn’t provided when the user began creating a new round.

We add a button to the bottom of the editor that takes the user to the tee set listing page, where the user can select a tee set to associate with the round:

  <s:button value="Select Tee Set" view="/TeeSetList.xhtml">
    <f:param name="from" value="RoundEdit"/>
  </s:button>

The from parameter is used by the tee set listing page to know where to send the user after a tee set is selected. (You could also store this information in the conversation context.) The default behavior is to show the detail page of the tee set, which we don’t want in this case. Recall that the <s:button> component passes the conversation token automatically, thus preserving the long-running conversation.

On the tee set listing page, each row has a Select link, which appends the teeSetId request parameter to the URL with the value of the identifier for the tee set in the current row. The tee set for the current row is bound to the iteration variable teeSet. The Select link is defined as follows:

  <s:link view="/#{empty from ? 'TeeSet' : from}.xhtml"
    value="#{empty from ? 'View' : 'Select'}">
    <f:param name="teeSetId" value="#{teeSet.id}"/>
  </s:link>

Once again, the <s:link> component takes care of passing along the conversation token. The RoundHome is still active in the conversation and awaiting the user’s return, which happens when the user clicks one of the Select links. When the RoundEdit.xhtml page is requested, the teeSetId is assigned to the TeeSetHome component, and the wire() method uses TeeSetHome to retrieve the selected TeeSet instance and assign it to the Round. These steps are exactly the same as those I described earlier when we were creating a new round. The only difference is that now the tee set is being wired to an instance of Round that’s managed by the persistence context (and is therefore in the database).

The benefit of navigating to the tee set listing page is that the user can use the search form to locate a tee set. However, this page flow presents a problem. If the user has made any changes to the values of the inputs in the form, those changes are lost when the user navigates away to select a tee set. That’s because the <s:button> component issues a GET request to navigate to the next page and does not submit the form, a point that I’ve raised many times in this book. You have two options for preserving the pending changes:

  • Submit the form before navigating
  • Periodically push the form values to the model using Ajax

The first option requires that you replace <s:button> with a UI command component:

  <h:commandButton value="Select Tee Set" action="selectTeeSet"/>

When a UI command button is used, the from parameter must be appended to the URL using a navigation rule in the RoundEdit.page.xml page descriptor:

  <navigation from-action="selectTeeSet">
    <redirect view-id="/TeeSetList.xhtml">
      <param name="from" value="RoundEdit"/>
    </redirect>
  </navigation>

The second option requires that you use an Ajax-enabled JSF component library such as Ajax4jsf or ICEfaces, which you’ll learn more about in chapter 12. Here’s an example of using the <a:support> component tag from Ajax4jsf to synchronize the changes in one of the fields with the component on the server when the field loses focus:

  <s:decorate id="scoreField" template="layout/edit.xhtml">
    <ui:define name="label">Total score:</ui:define>
    <h:inputText id="score" value="#{round.totalScore}">
      <a:support event="onblur" reRender="scoreField" ajaxSingle="true"/>
    </h:inputText>
  </s:decorate>

There’s a side effect to proactively pushing changes onto the managed entity (which occurs in the Update Model Values phase). The changes are flushed to the database before the user has finished editing the record. To avoid that situation, we need to switch to manual flushing.

Holding back changes

As you’ll recall from the previous two chapters, the persistence manager flushes the persistence context when the transaction is closed, or possibly sooner. To hold off the changes until the user explicitly requests that they be flushed to the database, we need to enable manual flushing of the persistence context. You can have Seam handle the switch by declaring a flush mode on the <begin-conversation> element in page descriptor (e.g., RoundEdit.page.xml) or on the @Begin annotation. Remember that to use manual flushing, you must be using Hibernate natively or as the JPA provider:

  <begin-conversation join="true" flush-mode="manual"/>

With this configuration in place, database transactions may come and go, but the changes to the entity instance aren’t migrating to the database until the flush() method on the persistence manager is called. The right time for these changes to be made is when the user clicks the Save, Update, or Delete button, signaling the end of an application transaction. Fortunately, you don’t have to worry about calling flush(). The persist(), update(), and remove() methods on Home take care of flushing the persistence context.

Selecting a tee set in place

After examining the navigation routine to select a tee set, you may be thinking that it would make more sense to have the user choose the tee set from a select menu within the editor form. That’s certainly a reasonable approach. The challenge is how to assign a value to a property whose type is an entity class using a JSF form input. As you learned in chapter 9, Seam’s entity converter, registered using the <s:convertEntity> tag, takes care of converting entity instances to and from their identifier values when used as options in a select menu. Keep in mind that this negotiation relies on the Seam-managed persistence context. Here’s how you define the form input for selecting a tee set in the round editor:

  <h:selectOneMenu value="#{round.teeSet}">
    <s:selectItems var="_teeSet" value="#{teeSets}"
      label="#{_teeSet.course.name} - #{_teeSet.color}"/>
    <s:convertEntity/>
  </h:selectOneMenu>

The request-scoped context variable teeSets supplies the collection of tee sets, something you learn to create with ease using the Query component in section 10.4. Note that it’s no longer necessary to wire in a tee set in the wire() method. With the form fields in the round editor populated, let’s take a look at the user’s exit strategies.

Reverting changes

There is one caveat to watch out for when working with an extended persistence context. If the user makes changes to the entity instance but doesn’t follow up with a save, update, or delete operation, the changes remain on the instance (i.e., it’s dirty) for the duration of the conversation, unless it’s refreshed from the database. The dirty instance will also be used in a result set retrieved in the same conversation if its identifier matches one of the results.

Consider the case when the user clicks Cancel and is redirected to the detail page by a navigation rule, but the navigation rule doesn’t end the conversation prior to the redirect, perhaps to avoid dropping JSF messages. Now there’s a chance that the data shown doesn’t coincide with the record in the database. This doesn’t jeopardize the data’s integrity, but it can mislead the user. What you want to do in this scenario is revert the managed entity back to its old self. There may be other use cases when you want to “reset” the pending changes.

Fortunately, the persistence manager includes a method that handles this task. The refresh() method synchronizes from the database to the entity instance, overwriting any changes that might have been made to the instance since being retrieved from the database. (Any collections containing transient entity instances must be first cleared.) This method is the exact opposite of persist(). First, we need to add a method to RoundHome that delegates to refresh() and clears the selected tee set:

  @Transactional
  public String revert() {
      getEntityManager().refresh(getInstance());
      teeSetHome.clearInstance();
      return "reverted";
  }

Next, this method is attached to the Cancel button at the bottom of the round editor:

   <s:button id="revert" value="Discard changes"
     action="#{roundHome.revert}" rendered="#{roundHome.managed}"/>

Finally, we need a navigation rule:

  <navigation from-action="#{roundHome.revert}">
    <end-conversation/>
    <redirect view-id="/Round.xhtml"/>
  </navigation>

When the user clicks the Discard Changes button, any changes are washed away and the data shown on the round detail page reflects the current values stored in the database.

It’s a good idea to provide the user with the ability to cancel cleanly like this on all CRUD forms. As an alternative, users always have the option of abandoning the conversation.

Nuking an instance

Although it would certainly fit, the D in CRUD doesn’t stand for Discard changes. As you know, the D stands for Delete. Fortunately, there’s nothing you have to do to implement the delete operation. The user can already click the Delete button that we added to the form in listing 10.2. This button activates the remove() method on Home, which delegates to the remove() method on the persistence manager to remove the instance from the database, putting the instance of Round back into the transient entity state. The only work required from you is to navigate the user somewhere afterward, such as the previous page or the listing of rounds:

  <navigation from-action="#{roundHome.remove}">
    <end-conversation/>
    <redirect view-id="#{roundFrom != '/Round.xhtml' ?
       roundFrom : '/RoundList.xhtml'}"/>
  </navigation>

That segues us into the next task, which is to fill in the remainder of the navigation rules to ensure the user is returned to an appropriate page after each CRUD operation is complete.

Wrapping things up

All that’s left is to add the remaining navigation rules, secure the page, and deal with exceptions. The navigation rule for the persist and update operations follows the same pattern used by the navigation rule for the remove operation. The navigation rule for the persist operation is shown here:

  <navigation from-action="#{roundHome.persist}">
     <end-conversation/>
     <redirect view-id="#{roundFrom != null ?
       roundFrom : '/Round.xhtml'}"/>
  </navigation>

The long-running conversation is terminated by the <end-conversation> element following the execution of each CRUD operation since these methods define the boundaries of our use case. Although the conversation is ended, the status messages queued by the CRUD methods still carry over to the next page since we aren’t ending the conversation before the redirect. You’ll learn how to configure the messages that the Home component produces in section 10.3.1.

We need to add the login-required restriction to the <page> node to ensure that the user is authenticated before creating or editing a round. In the next chapter, you’ll learn how to create more restrictive security rules.

  <page login-required="true">
    ...
  </page>

If an existing entity can’t be located in the database, the Home component raises the exception org.jboss.seam.framework.EntityNotFoundException, which results in a 404 error page being displayed as declared by the @HttpError annotation on the exception class. If a persistence exception is thrown, you can handle it in one of two ways. You can override the CRUD methods on Home and implement a try/catch block, or you can register an exception handler in the global page descriptor to direct the user to an error page as follows:

  <exception class="javax.persistence.OptimisticLockException">
    <redirect view-id="/error.xhtml">
      <end-conversation/>
      <message>The record was modified by another user.</message>
    </redirect>
  </exception>
  <exception class="javax.persistence.PersistenceException">
    <redirect view-id="/error.xhtml">
      <message>The operation failed. Please try again.</message>
    </redirect>
  </exception>

You’ve now seen the key aspects of the Home component and how it’s used to implement a CRUD scenario. Although there wasn’t a lot of Java code, you did have to write code to make it work (the wiring of the tee set and the revert logic). If you’re one of those people who would rather “program in XML”—you know who you are—you’ll be excited to hear that you can commission a Home while steering clear of Java. To demonstrate, we add a new feature that lets a golfer add a review to a golf course.

10.2.5. CRUD a la XML

A Home component is “programmed” in XML by declaring and configuring it in the component descriptor. You primarily interact with its instances using the EL. If you do intend on injecting the instance into another component, the receiving property’s type must be EntityHome (or HibernateEntityHome) parameterized with the entity class since there’s no application-specific subclass:

  private EntityHome<RoundHome> roundHome;

As you might imagine, defining a component entirely in XML is best suited for relatively straightforward use cases. In this case, you are merely the Home’s puppeteer. You can pull its strings in different ways, but your options are limited. Fortunately, the EL and the component descriptor give us enough flexibility to establish relationships for the entity instance that the Home manages. This wiring is done by creating a prototype for the entity using component configuration, then passing that prototype to the newInstance property on Home, the approach followed in this next example.

From the course detail page you’ll provide a form to let users comment on a course. The course detail page was built by seam-gen in chapter 2. All we need to do in this section is define the CourseComment entity class, create a Home component to manage it, and add a new form to the bottom of the course detail page where the comment is entered. We assume that the author of the comment is the current golfer.

Let’s start with the CourseComment entity class, shown in listing 10.5.

Listing 10.5. The entity class representing a comment on a course
  package org.open18.model;
  import ...;

  @Entity
  @Table(name = "course_comment")
  public class CourseComment implements Serializable {
      private Long id;
      private Integer version;
      private Date datePosted;
      private String text;
      private Course course;
      private Golfer golfer;

      @Id @GeneratedValue
      public Long getId() { return id; }
      public void setId(Long id) { this.id = id; }

      @Version
      public Integer getVersion() { return version; }
      private void setVersion(Integer version) { this.version = version; }

      @Temporal(TemporalType.TIMESTAMP)
      public Date getDatePosted() { return datePosted; }
      public void setDatePosted(Date date) { this.datePosted = date; }
      @Lob
      public String getText() { return text; }
      public void setText(String text) { this.text = text; }

      @ManyToOne(fetch = FetchType.LAZY) @NotNull
      @JoinColumn(name = "COURSE_ID", nullable = false)
      public Course getCourse() { return course; }
      public void setCourse(Course course) { this.course = course; }

      @ManyToOne(fetch = FetchType.LAZY) @NotNull
      @JoinColumn(name = "GOLFER_ID", nullable = false)
      public Golfer getGolfer() { return golfer; }
      public void setGolfer(Golfer golfer) { this.golfer = golfer; }
  }

Next, we configure a prototype that initializes a transient instance of CourseComment, inject the value expression for the prototype into the Home component that manages this entity, and finally define an alias for the getInstance() method:

  <component name="courseCommentPrototype"
    class="org.open18.model.CourseComment">
    <property name="datePosted">#{currentDatetime}</property>
    <property name="course">#{courseHome.instance}</property>
    <property name="golfer">#{currentGolfer}</property>
  </component>

  <framework:entity-home name="courseCommentHome"
    entity-class="org.open18.model.CourseComment"
    new-instance="#{courseCommentPrototype}"/>

  <factory name="courseComment" value="#{courseCommentHome.instance}"/>

The expression #{currentDatetime} references a built-in component provided by the Seam Application Framework that resolves to a SQL-compliant timestamp representing the time it is resolved. Seam also has built-in components that resolve to the current SQL-compliant date and SQL-compliant time, currentDate and currentTime, respectively.

All that’s left is to define the form for creating a comment. The form is only rendered if the user is logged so that there’s someone to blame for the comment.

  <h:form id="commentForm" rendered="#{currentGolfer != null}">
    <rich:panel><f:facet name="header">Leave a comment</f:facet>
      <s:decorate id="textField" template="layout/edit.xhtml">
        <ui:define name="label">Comment:</ui:define>
        <h:inputTextarea id="text" value="#{courseComment.text}"/>
      </s:decorate>
      <div class="actionButtons">
        <h:commandButton id="save" value="Post"
          action="#{courseCommentHome.persist}"/>
      </div>
    </rich:panel>
  </h:form>

You’re done! With all the time you saved, you can spend the rest of the day making the application prettier for your boss. Granted, not all forms are going to be this simple, but that’s why you can mix and match the XML configuration with Java at the component or application level. When the forms are this simple, you should be glad to know you can simply whip out the component descriptor and get the job done in no time flat. When things are more complex, you can use the Java API and earn your living. If complex coding is required, your best bet may be to use Groovy!

Amid all the CRUD that has taken place, we’ve overlooked one very important detail. We’re pleased that data is being persisted to the database, but we aren’t letting the user know how things are going. Let’s see how to keep the user informed after the submit.

10.3. Providing feedback

Communication is important. That’s why the Home component supports two ways of providing feedback. The first is a success message that’s displayed to the user. The second is a set of internal events that notify other components of the transaction’s completion. This section explores these two communication mechanisms.

10.3.1. Customizing the status messages

The Home component prepares a generic, info-level status message after successful completion of any CRUD operation. At the time of this writing, the messages generated are specific to JSF, though in the future, the controller classes in the Seam Application Framework will produce status messages appropriate for the UI framework being used. All you have to do is display the message on the ensuing page:

  <h:messages globalOnly="true"/>

But who wants generic messages? I’m sure you want to give the users lots of good information so that it’s very clear to them what happened. Let’s customize the CourseHome component generated by seam-gen in chapter 2 to give the user personalized messages rather than the canned responses that Seam provides.

As you’ve learned, Seam is very flexible when it comes to message handling, especially since you can use EL notation in message templates to reference contextual data. Messages are added to the response using the built-in FacesMessages component. One way to access this component is by calling the getFacesMessages() method on any Controller component. The FacesMessages component allows you to

  • Use EL notation in message templates to take advantage of context variables
  • Load message templates from a Seam-managed resource bundle for i18n support
  • Configure a fall-back message string to use if a key isn’t found in the resource bundle

There are two ways to override the message templates used by the Home component. You can either define them directly on the component or you can place them in a resource bundle. Listing 10.6 shows the CourseHome component, which sets up custom message templates in the create() method. This method is called immediately after the component is instantiated, as indicated by the @Create annotation inherited from the overridden method. The message templates all use the value expression #{course}, which is satisfied by the @Factory method on the same component.

Listing 10.6. The CourseHome component configured to use custom messages

Alternatively, you could set these messages using component configuration:

  <framework:entity-home name="courseHome"
    class="org.open18.action.CourseHome"
  created-message="You've successfully added #{course.name}. Thanks for contributing!"
  updated-message="Thanks for updating #{course.name}. Your careful eye is appreciated!"
  deleted-message="&#8205;#{course.name} has been removed. We never liked it anyway."/>

 

Note

Notice the use of &#8205; at the start of the deleted-message attribute. Seam evaluates strings that begin with the character sequence #{ at the time they’re assigned to the property. This XHTML entity reference, a zero-width space, offsets the first character, allowing the evaluation to be deferred until the status message is created.

 

The limitation of configuring the message templates directly on the component is that, even though the templates have dynamic parts, only a single language is supported. Let’s see how to define message templates that can be selected according to the user’s locale.

10.3.2. Creating i18n-compliant messages

To enable internationalization (i18n) support for the success messages, you define them in the Seam resource bundle. Refer back to section 5.5.2 in chapter 5 to see how to configure the Seam resource bundle. Fortunately, you don’t have to establish a connection between the Home component and the resource bundle since logic is built-in to the Home class to consume messages in this bundle.

Before consulting the messages defined on its own message properties, Home looks for message bundle keys associated with the entity class being managed. It assembles the message key by combining the simple name of the entity class (as returned by the getSimpleName() method on the class object) with the operation being performed, separated by an underscore (_), as illustrated in figure 10.8.

Figure 10.8. The way Home assembles a message bundle key for a CRUD operation.

Here are the English message bundle keys for the CourseHome component that were configured previously, now defined in messages_en.properties:

  Course_created=You've successfully added #{course.name}. Thanks for contributing!
  Course_updated=Thanks for updating #{course.name}. Your careful eye is appreciated!
  Course_deleted=#{course.name} has been removed. We never liked it anyway.

If the message key can’t be found in the Seam resource bundle, the Home component falls back to using the message templates configured on the component or, if those aren’t set, the built-in messages. In section 13.6.1 in chapter 13 you’ll learn how the default language is selected and how to create a UI control that allows users to change the language for their session. In addition to keeping the user informed, Seam broadcasts the success of the CRUD operations to other components using its event facility.

10.3.3. Transaction success events

When a CRUD operation completes successfully, so does a transaction. The Home component schedules two events to be raised when the transaction is committed, using the raiseAfterTransactionSuccessEvent() method. The first is a generic event indicating the success of the transaction, duplicating the org.jboss.seam.afterTransactionCompletion event raised by the Seam transaction infrastructure. The second event is customized to the simple name of the entity class whose persistence state is being modified. Unfortunately, neither event tells you which operation was performed. If this were the RoundHome component, the events would be as follows:

  • org.jboss.seam.afterTransactionSuccess
  • org.jboss.seam.afterTransactionSuccess.Round

The second event can be used to refresh a result set which may now hold a stale reference to the modified entity. Assuming the result set is managed by a roundList component, introduced in the next section, you can bind its refresh methods to the transaction success event using the component descriptor:

  <event type="org.jboss.seam.afterTransactionSuccess.Round">
    <action execute="#{roundList.refresh}"/>
    <action execute="#{roundList.getResultList}"/>
  </event>

What’s unique about these events is that they aren’t raised immediately, but only after the transaction completes. If you’re using Seam’s global transactions, the commit happens at the end of the Invoke Application phase. This scheduling is handled by registering these events using transaction synchronization, an interface that allows callback code to be executed by the transaction. You learned in the previous chapter that Seam allows transaction synchronizations to be used even when using resource-local transactions.

In this section, you’ve learned to appreciate that the Home class is much more than a generic CRUD interface. It can be a stateful component and active domain model object that wraps an entity instance, manages its state, and provides declarative transaction boundaries around the CRUD operations performed on it. It can also coordinate with other Home components to establish associations to other entity instances. To add polish, it even prepares success messages for the user and raises transaction completion events to notify other components when the transaction completes.

While the Home component manages a single entity instance, the component template we look at next manages the result set of a JPQL or HQL query. You can even create stateful lists just as you’ve created stateful domain objects.

10.4. Smarter queries with the Query component

When you introduce queries into your application, you’re immediately faced with the decision of how to manage the result set. Knowing the right time to execute the query can be difficult to determine. If you execute the query every time you need to present the results, you put undo pressure on the database. At the other extreme, if you hold onto the results for too long, you end up feeding users with stale information that may confuse them, or worse, cause them to make wrong decisions. Once again, you need a strategy.

Fortunately, the Seam Application Framework includes a class that helps you manage the results of a query, appropriately named Query. As you soon discover, the Query component manages contextual queries, which means that the query can change dynamically as its parameters (which are mapped to context variables) change. Just like the Home component template, there are implementations of the Query class for both JPA and Hibernate, EntityQuery and HibernateEntityQuery, respectively. The class diagram for the EntityQuery component is shown in figure 10.9. This section shows you how to use the Query component, focusing on the JPA implementation.

Figure 10.9. The class diagram of EntityQuery

As with the Home component, you can use the Query component either by extending the Query class, configuring it directly in the component descriptor, or a combination of the two. Query is a sufficiently flexible component template, so typically there isn’t a need to write custom Java code. Let’s build on the example in the previous section by creating a page that lists all the Round instances stored in the database. We use a Query component to manage the result set.

10.4.1. Creating a result set listing

To use the Query component, at a minimum, you must supply it with an entity query, written in JPQL (when using JPA) or in HSQL (when using native Hibernate). You assign the query to the component’s ejbql property (despite the name of the property, it isn’t specific to JPA).

The declaration of the Query component used to manage the list of rounds is shown here:

  <framework:entity-query name="roundList" ejbql="select r from Round r"/>

The ejbql property holds the static portion of the query. This fragment includes the select clause, join operators, and static conditions. The contextual restrictions are added in a later section. Rather than pulling all of the rounds, let’s set a limit of 15 using the maxResults property of the Query class, which you’ll eventually bind to a UI control:

  <framework:entity-query name="roundList" ejbql="select r from Round r"
    max-results="15"/>

Preparing and executing the query is handled entirely by the Query class, which delegates the work of running the query to the persistence manager. Three operations are supported by the query: You can query for a list of results, a single result, or the result count. If you’re only expecting one result and more than one result is found, a NonUniqueResultException is thrown. The Query class also includes a convenience method for wrapping the result set in a JSF DataModel. The methods for retrieving the result set data are shown in table 10.4.

Table 10.4. The methods on Query that execute JPQL/HQL queries

Method

Description

getResultList() Executes the query if a local result set isn’t available. The result set is stored to avoid redundant queries. Returns the result set as a java.util.List.
getSingleResult() Executes the query if a local result value isn’t available. The result value is stored to avoid redundant fetches. Returns the result as an object.
getResultCount() Executes the count equivalent of the query if a result count isn’t available. The count is stored to avoid redundant fetches. Returns the row count as a java.lang.Long. May require customization if the query is complex.
getDataModel() Wraps the return value of getResultList() in the appropriate javax.faces.DataModel type and stores it until the query is executed again. Returns the wrapped value.

The query methods listed in table 10.4 avoid redundant database queries by caching the results in a private property on the class. The query isn’t executed again until the component considers the state of the results to be “dirty,” as defined by any of the following conditions:

  • A query restriction parameter changes.
  • The sort order changes.
  • The maximum result value changes.
  • The first result offset changes.
  • The results are manually cleared by a call to refresh() on Query.

The retention of the query results is especially important for components that are used in JSF templates since the encoding and decoding process of a JSF component tree can cause methods in table 10.4 to be executed many times. Ensuring that the query only executes when necessary avoids taxing the database.

By default, an instance of Query is scoped to the event context. If you scope it to the conversation context instead, the caching of the result is able to span multiple requests:

  <framework:entity-query name="roundList" scope="conversation"
    ejbql="select r from Round r" max-results="15"/>

Caching the result does run the risk of stale data, but thanks to the aforementioned dirty checks the Query class ensures that the query is executed at the appropriate time. An event can also be used to refresh the query, as demonstrated earlier.

The roundList component is now ready to be used to display the collection of Round instances in a data table. The relevant portions of the RoundList.xhtml JSF template that renders these results are shown in listing 10.7.

Listing 10.7. A table showing the results of the query that fetches the list of rounds
  <rich:panel><f:facet name="header">Round search results</f:facet>
    <h:outputText value="No rounds were found"
      rendered="#{empty roundList.resultList}"/>
    <rich:dataTable var="_round" value="#{roundList.resultList}"
      rendered="#{not empty roundList.resultList}">
      <h:column>
        <f:facet name="header">Golfer</f:facet>
        #{_round.golfer.name}
      </h:column>
      <h:column>
        <f:facet name="header">Date</f:facet>
        <h:outputText value="#{_round.date}">
          <s:convertDateTime type="date"/>
         </h:outputText>
       </h:column>
       <h:column>
         <f:facet name="header">Course</f:facet>
         #{_round.teeSet.course.name}
       </h:column>
       <h:column>
         <f:facet name="header">Tee set (color)</f:facet>
         <div title="#{_round.teeSet.color}" class="colorSwatch"
           style="background-color: #{_round.teeSet.color};"/>
       </h:column>
       ...
       <h:column>
         <f:facet name="header">action</f:facet>
         <s:link id="round" view="/Round.xhtml" value="View">
           <f:param name="roundId" value="#{_round.id}"/>
         </s:link>
       </h:column>
     </rich:dataTable>
   </rich:panel>

Notice in listing 10.7 how the lazy associations on the Round entity are casually traversed. Once again, it’s a good idea to optimize the initial query to prevent excessive querying in the view. The first optimization is to tune the persistence manager to anticipate lazy fetches and batch fetch the data. In Hibernate, this behavior is configured by setting the default batch fetch size in the persistence unit configuration:

  <property name="hibernate.default_batch_fetch_size" value="16"/>

Play around with the batch size while monitoring the SQL statements logged by Hibernate to see how it affects the number of queries that are executed. The other approach you can take is to modify the query to fetch associations eagerly. If the association mapping is marked as FetchType.LAZY, it can be temporarily promoted to eager by using the join fetch clause. The following query grabs all the information needed on the round list page in one aggregate query:

  select r from Round r
    join fetch r.golfer
    join fetch r.teeSet ts
    join fetch ts.course

I recommend always using lazy associations in the mapping and enabling eager fetching at the query level. For other ways of cutting down on lazy-loading queries, consult the reference documentation for your persistence provider. To help your DBA find the source of a SQL statement, you can add a comment to the query using a query hint:

  <framework:entity-query ...>
    <framework:hints>
      <key>org.hibernate.comment</key>
      <value>Query for rounds used on RoundList.xhtml</value>
    </framework:hints>
  </framework:entity-query>

The hints property accepts any hint that the persistence provider supports. Let’s see what else we can do with the query.

10.4.2. Paging the result set

The RoundList.xhtml page has one serious flaw at the moment. If there are more than 15 results, there’s no way to see beyond the first page. The UI needs to give the user a way to paginate to other regions of the query. It should come as no surprise that the Query class has built-in support for pagination. Query paging is controlled by the firstResult and maxResults properties on this component, which alter the underlying JPQL/HQL to load the corresponding region of the result set. Anytime either of these properties change, the result set is refreshed. Table 10.5 lists the methods on the Query class that assist in creating UI controls to manipulate the value of these two properties.

Table 10.5. The methods on Query that provide information about pagination

Method

Description

isNextExists() Indicates whether more results exist beyond the current page
isPreviousExists() Indicates whether results exist before the current page
getNextFirstResult() Returns the offset of the first result on the next page
getPreviousFirstResult() Returns the offset of the first result on the previous page
getLastFirstResult() Returns the offset of the first result on the last page
getPageCount() Returns the number of pages in the result set, using the maximum results setting as the page size

The logic in the Query class is able to extract the pagination offset information without having to use a second query. While most of the methods are simple calculations based on the current offset value, isNextExists() is a special case. The Query class avoids using an extra query by always fetching one more record than the page size (i.e., the maxResults value). If the extra record is present in the result set, the Query class knows another page is available. It then truncates the result set back to the page size to remove trace of this “feeler” record. The getPageCount() is the one method that requires an extra query to be executed, but only if the page size is non-null, because it needs to know the total number of records in the database.

With the pagination information in hand, there’s still the matter of keeping track of pagination offset between requests. If you aren’t using a long-running conversation on the listing page, then the offset must be passed as a request parameter. This strategy is accomplished by combining a page parameter with links that include the offset in the query string. Begin by declaring a page parameter in the RoundList. page.xml descriptor:

  <page>
    <param name="firstResult" value="#{roundList.firstResult}"/>
  </page>

Next, add links for the user to navigate between pages. The following link advances the user to the next page in the result set, if it’s available:

  <s:link id="next" value="Next Page" rendered="#{roundList.nextExists}">
    <f:param name="firstResult" value="#{roundList.nextFirstResult}"/>
  </s:link>

You can see examples of this approach in the listing pages generated by seam-gen in chapter 2. Page parameters are great because they create RESTful URLs. However, they break down when the user has to navigate away from the page because they get dropped. Another way to maintain the state of the offset is to use a long-running conversation.

The benefit of using a long-running conversation with a conversation-scoped Query component is that the user is free to roam the application without losing the current position in the result set. If the user manages to abandon the conversation, it can be restored using a conversation switcher, revealing that the result set is in the exact state the user left it in.

You have to determine whether it’s more important to you to produce RESTful URLs or for the state of the query to be maintained. While it’s possible to implement both simultaneously, it takes some work. For the remainder of the chapter, we work within the context of a long-running conversation to demonstrate the stateful approach.

The first step to creating a stateful Query component is to begin a long-running conversation when the round list page is rendered, which is defined in the RoundList.page.xml descriptor. The page is also given a description to allow the user to return to this conversation using a conversation switcher:

  <page>
    <description>
      Round List: #{roundList.resultList.size} of #{roundList.resultCount}
    </description>
    <begin-conversation join="true"/>
  </page>

Now that the roundList is maintained in a long-running conversation, it’s no longer necessary to set the pagination offset explicitly. Instead, it’s possible to use the builtin pagination methods on the Query class, listed in table 10.6. These actions take care of setting the firstResult property internally and also resetting the cached result set when called.

Table 10.6. The methods on Query that paginate the result set

Method

Description

next() Advances the first result value to the offset of the next page
previous() Reverts the first result value to the offset of the previous page
last() Advances the first result value to the offset of the last page
first() Reverts the first result value to the offset of the first page, which is always 0

All that’s left is to add command links that execute the pagination action methods. The links must be nested within a JSF form as required by the JSF specification. Between the links is a select menu to change the page size. A value change listener is used on the select menu to reset the pagination offset when the page size is changed:

  <div id="tableControl">
    <h:form id="pagination">
      <h:commandLink id="first" action="#{roundList.first}"
        value="First Page" rendered="#{roundList.previousExists}"/>
      <h:commandLink id="previous" action="#{roundList.previous}"
        value="Previous Page" rendered="#{roundList.previousExists}"/>
      <h:selectOneMenu id="pageSize" value="#{roundList.maxResults}"
        valueChangeListener="#{roundList.first}"
        onchange="this.form.submit();">
        <f:selectItem itemValue="25"/>
        <f:selectItem itemValue="50"/>
      </h:selectOneMenu>
      <h:commandLink id="next" action="#{roundList.next}"
        value="Next Page" rendered="#{roundList.nextExists}"/>
       <h:commandLink id="last" action="#{roundList.last}"
         value="Last Page" rendered="#{roundList.nextExists}"/>
     </h:form>
   </div>

Now that the user has access to all the rounds in the database and the list remains stable on postback, it’s the perfect opportunity to implement multirow deletions.

10.4.3. Deleting multiple records at once

Let’s steer slightly off topic for a moment to implement a CRUD feature that wasn’t possible earlier in the chapter. Any operation on multiple records at once is typically done from the listing page. Fortunately, the process is straightforward. First, add a transient boolean property to the Round class that indicates whether the record is selected:

  private boolean selected;

  @Transient public boolean isSelected() { return this.selected; }
  public void setSelected(boolean selected) { this.selected = selected; }

Next, add a new column to the listing table with a checkbox for selecting a record:

  <h:column>
    <h:selectBooleanCheckbox value="#{_round.selected}"/>
  </h:column>

Then, add a button below the list that invokes the delete() method when clicked:

  <h:commandButton action="#{multiRoundAction.delete}"
  value="Delete selected"/>

Finally, implement the delete() method on the new MultiRoundAction component. For fun, we implement the component in Groovy, naming the file MultiRoundAction.groovy.

  @Name("multiRoundAction")
  class MultiRoundAction {
      @In private def entityManager
      @In private def roundList
      void delete() {
          roundList.resultList.findAll { r -> r.selected }
              .each { r -> entityManager.remove r }
          roundList.refresh()
          "/RoundList.xhtml"
      }
  }

The types on the properties are not required since Seam uses name-based injections. Let’s return to managing the query by giving the user the ability to sort the results by clicking on the column headers.

10.4.4. Putting the results in order-

The Query class has built-in support for sorting the result set. The sort order is maintained in a property named order, which holds both the sort column and sort direction, and is appended to the managed JPQL/HQL query. Whenever the order property changes, the cached result set is invalidated and the query is executed again.

Keep in mind that the Query class sanitizes the order property to check for SQL injection. Seam 2.1 increases the resilience to SQL injection by splitting the order property into orderColumn and orderDirection, which I strongly encourage you to use.

Let’s start by establishing a default sort by assigning a value to the order property:

  <framework:entity-query name="roundList" scope="conversation"
    ejbql="select r from Round r
      join fetch r.golfer g
      join fetch r.teeSet ts
      join fetch ts.course c"
    max-results="15" order="r.date desc"/>

The result set of this query will be sorted by the date of the round in descending order. The date property on Round is qualified in this query as r.date to distinguish it from a property named date on any other entity in the query. It’s always a good idea to qualify the name of the property by prefixing it with the alias defined in the select clause. The aliases in this query are r for Round, g for Golfer, ts for TeeSet, and c for Course. These aliases will be used throughout the remainder of this chapter. To sort on the name of the golfer, you’d set the order property to g.lastName asc, g.firstName asc.

Once again, your job is to provide the user with a UI control, this time to assign the order property of the Query component. As is standard practice, we make each column header a sort link. In our case, the link will pass the sort clause to the Query class’s setOrder() method using a parameterized method expression in the action of the UI command component. Here’s the link in the column header for the course name column:

  <s:link value="Course Name"
    styleClass="#{roundList.order == 'c.name asc' ? 'asc' :
      (roundList.order == 'c.name desc' ? 'desc' : '')}"
    action="#{roundList.setOrder(roundList.order eq
      'c.name asc' ? 'c.name desc' : 'c.name asc')}"/>

Two bits of logic are performed by this component tag. In the parameter of the action method, a check is performed to determine if the sort needs to be reversed or the default sort applied, depending on whether the column is currently sorted. In the styleClass attribute, a similar check is performed to determine if this column is sorted and, if so, the direction of the sort. The work of rendering a sort indicator is left up to two CSS classes, shown here:

  th a.asc {
      background-image: url(../img/sort_asc.gif);
  }
  th a.desc {
      background-image: url(../img/sort_desc.gif);
  }

Since this markup has to be reproduced for every column, it’s just screaming to be converted to a Facelets composition template,[4] which you learned about in chapter 3.

4 For more information regarding how to define and use Facelets composition templates, please refer to the Facelets reference documentation at https://facelets.dev.java.net/nonav/docs/dev/docbook.html.

Let’s push this logic into the template layout/sort.xhtml to encapsulate the complexity of this link:

  <ui:composition ...>
    <h:commandLink value="#{name}" action="#{query.setOrder(param.order)}"
      styleClass="#{query.order == property.concat(' asc') ? 'asc' :
      (query.order == property.concat(' desc') ? 'desc' : '')}">
      <f:param name="order" value="#{query.order ==
        property.concat(' asc') ? property.concat(' desc') :
        property.concat(' asc')}"/>
    </h:commandLink>
  </ui:composition>

Now, the logic of the sort link is only defined in one place. However, some changes had to be made to accommodate Facelets: We are now using a standard UI command link and there’s extensive use of parameterized EL. But all of that is behind you now. All you have to do is fill in the template parameters. Here’s the sort link for the course name column again:

  <s:decorate template="layout/sort.xhtml">
    <ui:param name="query" value="#{roundList}"/>
    <ui:param name="name" value="Course Name"/>
    <ui:param name="property" value="c.name"/>
  </s:decorate>

That takes care of sorting! The Query class handles the task of applying the order clause to the JPQL/HSQL query. If you need features like multicolumn sort or column reordering, I recommend using an advanced table component from a JSF component library instead.

Query paging and sorting only scratches the surface of the Query component’s capabilities. The most powerful feature is conditional restrictions. Let’s explore this feature by implementing a form that allows the user to search for rounds.

10.4.5. Placing restrictions on the result set

Paging through hundreds of results can take its toll on your user. To make for a better experience, you need to let users help themselves to the data by giving them a way to supply criteria that pares down the result set. Searching is one of those tasks that has been long dreaded by developers because it almost always means building dynamic queries. If you’ve ever had to maintain code that uses a custom SQL builder to implement a search page, you can appreciate how much pain is involved. That’s why effort was put into establishing an intelligent restriction mechanism for the Query component.

Restrictions as a built-in query builder

Restrictions are assigned to the Query component as a collection of conditions, each having exactly one embedded value expression. At runtime, the restrictions are adjoined to the where clause of the JPQL/HQL query using the AND operator. Thus, each restriction works to limit the result set. Here’s an example restriction that searches by the golfer’s last name:

  g.lastName = #{roundExample.golfer.lastName}

We’re going to get to the roundExample context variable in a moment. What’s important to recognize is that the condition is derived from an EL value expression. Each restriction must have exactly one value expression embedded in it. That value expression is the equivalent of a query parameter, but with one crucial enhancement. If the expression resolves to null or an empty string value, the restriction is omitted from the query. That’s how Seam is able to formulate dynamic queries using restrictions.

Aside from the obvious benefit of being able to reference context variables in your queries, using the EL gives Seam the opportunity to prepare the values as query parameters so they’re properly escaped. That way, your application isn’t vulnerable to SQL injection attacks. What’s nice about the restriction facility in general is that query building is just a matter of configuration, rather than yet another hand-built solution.

The restrictions are stored as a collection of strings on the restrictions property of Query. You can initialize the collection in Java or assign the values using component configuration. The remainder of this section explores various ways to apply the restrictions.

Query by example

Restrictions are a way to make a JPQL/HQL query contextual. The context—or state, in this case—is the criteria the user has entered in the search form. To get the criteria values from the form to the query, they need to be bound to the properties of a component instance. The Query by Example (QBE) pattern lends itself nicely to this problem. In QBE, you pass a criteria object to the query engine and tell it to “find results like this.” The object that you pass to the query engine is a partial representation of the objects in the result set. Since the results in the listing page are instances of an entity class, in this case Round, then the example object must be an instance of Round.

Let’s create a new component role for Round named roundExample that’s fed as the example criteria for the round search. It’s scoped to the conversation, the default scope for an entity class, so that the criteria doesn’t get lost when the user paginates or sorts the result set or navigates away from the listing page.

The properties on this criteria object will be referenced in the restriction clauses on the Query component for the round list page. However, just searching on the properties of the Round entity is going to be limiting. Thus, we need to build up a hierarchical example object that can be fed into the join query introduced in the previous section. We create several additional component roles and wire instances of them together using component configuration:

  <component name="teeSetExample" class="org.open18.model.TeeSet"/>
  <component name="golferExample" class="org.open18.model.Golfer"/>
  <component name="roundExample" class="org.open18.model.Round">
    <property name="golfer">#{golferExample}</property>
    <property name="teeSet">#{teeSetExample}</property>
  </component>

With the example object ready to go, let’s put it to use building restriction clauses. We start small by allowing the user to perform a case-insensitive wildcard search on the name of the golfer and the color of the tee set played:

  <framework:entity-query name="roundList" ...>
    <framework:restrictions>
      <value>
        lower(g.firstName) like
          concat(lower(#{roundExample.golfer.firstName}),'%')
      </value>
      <value>
        lower(g.lastName) like
          concat(lower(#{roundExample.golfer.lastName}),'%')
      </value>
      <value>
        lower(ts.color) like
          concat(lower(#{roundExample.teeSet.color}),'%')
      </value>
    </framework:restrictions>
  </framework:entity-query>

The restrictions comprise the where clause of the JPQL/HQL query. The entity properties in the restriction must be qualified to the entity alias to which they belong. For instance, in the first restriction, the g prefix in g.firstName is an alias to the Golfer entity. When defining a restriction, you have the full power of JPQL at your disposal. That means you can use built-in JPQL/HQL functions (not SQL functions) such as concat() and lower() to customize the condition, as shown earlier. Unfortunately, you can’t apply two different value expressions in the same restriction. In that case, you probably need to rethink the problem or consider if you’ve outgrown the intentionally focused restriction facility, perhaps graduating to Hibernate Search.

The restrictions are one half of the equation. The other is the search form. The properties of the example object are bound to the inputs in the search form to capture the criteria values from the user:

  <h:form id="roundSearch">
    <rich:panel><f:facet name="header">Round search parameters</f:facet>
        <s:decorate id="firstNameField" template="layout/display.xhtml">
          <ui:define name="label">First name:</ui:define>
          <h:inputText id="firstName"
            value="#{roundExample.golfer.firstName}"/>
        </s:decorate>
        <s:decorate id="lastNameField" template="layout/display.xhtml">
          <ui:define name="label">Last name:</ui:define>
          <h:inputText id="lastName"
            value="#{roundExample.golfer.lastName}"/>
        </s:decorate>
        <s:decorate id="colorField" template="layout/display.xhtml">
          <ui:define name="label">Tee set color:</ui:define>
          <h:inputText id="color" value="#{roundExample.teeSet.color}"/>
        </s:decorate>
    </rich:panel>
    <div class="actionButtons">
      <h:commandButton id="search" value="Search"
        actionListener="#{roundList.first}"/>
    </div>
  </h:form>

Notice the #{roundList.first} method expression used in the action listener of the UI command component that submits the form. This action listener ensures that the pagination offset is reset before the search is performed. Although the Query class clears the result set when it detects a change to the restrictions, it doesn’t reset the pagination offset. It’s important to rewind the offset back to the first page because it ensures that if the search criteria were to reduce the size of the result set, the pagination offset wouldn’t be left beyond the last result. If that happened, no results would be displayed, even though results might have been returned by the query. To avoid putting the user in this confusing situation, we introduce the minor inconvenience of resetting the pagination.

Since restrictions are joined using the AND operator, if the user fills in a value for first name, last name, and tee set color, a record must match all of these conditions to be included in the result set. Query doesn’t have built-in support for the OR operator, though you can find an insider trick in the example code to get partial support.

So far, we’ve only used string-based properties in the restriction clause. In addition to primitive types, JPQL and HQL support complex types. Let’s start with dates.

Do you speak calendar?

It’s possible to use a value expression that resolves to a java.util.Date object directly in the JPQL/HQL query, and hence the restriction clause, just like any other primitive type. This will be demonstrated by allowing the user to filter the rounds within a date range. However, the Round entity class doesn’t have a way to represent a date range. Alas, we’ve outgrown the basic QBE use case. We introduce a new criteria object, RoundCriteria, that can host property values that cannot be captured by the entity:

  @Name("roundCriteria")
  @Scope(ScopeType.CONVERSATION)
  public class RoundCriteria {
      private Date beforeDate;
      private Date afterDate;

      public Date getBeforeDate() { return this.beforeDate; }
      public void setBeforeDate(Date date) { this.beforeDate = date; }

      public Date getAfterDate() { return this.afterDate; }
      public void setAfterDate(Date date) { this.afterDate = date; }
  }

Next, add the restrictions to the roundList component definition:

  <value>r.date &gt;= #{roundCriteria.afterDate}</value>
  <value>r.date &lt;= #{roundCriteria.beforeDate}</value>

If either date filter resolves to null, the date range will be open ended on that side. Notice that when you’re defining restrictions in the component descriptor, less-than and greater-than signs must be escaped. Finally, add the date input fields on the search form:

  <s:decorate id="afterDateField" template="layout/display.xhtml">
    <ui:define name="label">From:</ui:define>
    <rich:calendar id="afterDate" datePattern="MM/dd/yyyy"
      value="#{roundCriteria.afterDate}"/>
  </s:decorate>
  <s:decorate id="beforeDateField" template="layout/display.xhtml">
    <ui:define name="label">To:</ui:define>
    <rich:calendar id="beforeDate" datePattern="MM/dd/yyyy"
      value="#{roundCriteria.beforeDate}"/>
  </s:decorate>

The date filter example helps emphasize how convenient it is to attach a value from a nonprimitive UI input component to a query with little effort. The converting and formatting is handled for you and it just works. Next, you discover the same is true for collections.

Any of these will do

Like SQL, JPQL/HQL queries support the IN operator to find rows with a column value that matches any one of a collection of parameter values. This feature is often combined with “pick lists” where the user is presented with a set of options from which the search values can be selected. Searching on collections of simple types such as strings and numbers is fairly straightforward. What makes JPQL/HQL, and in turn the restriction clauses, so powerful is that the values in this collection can be entity instances, not just primitive values.

In the next example, the user is presented with a list of courses that can be used to filter the rounds by the courses selected. As you know, you can use entity instances in the options of a UI select menu when combined with the <s:convertEntity> converter tag. So far, we have used this technique to “wire” one entity instance to another. Now, we take this a step further by combining <s:convertEntity> with <h:selectManyListbox> to assign a collection of selected entity instances to the collection bound to the input. That collection will then be used in a restriction clause of the roundList component. To support these search criteria, a new java.util.List property is added to RoundCriteria to capture a collection of selected Course entity instances:

  private List<Course> courses;

  public List<Course> getCourses() { return this.courses; }
  public void setCourses(List<Course> courses) { this.courses = courses; }

Note that JSF can only process a multivalue selection that is bound to an array property or a parameterized collection property that extends java.util.List. You cannot bind to a java.util.Set property, for instance.

Next, we add a restriction that uses the courses property within an IN operator:

  <value>c IN(#{not empty roundCriteria.courses ?
    roundCriteria.courses : null})</value>

 

Note

An explicit check for an empty collection must be performed or an empty IN() clause is generated, resulting in a SQL error.

 

You may wonder how JPA manages to stuff a whole entity instance into a SQL query. Actually, it doesn’t. When entities are compared in a JPQL/HQL query, the query is rewritten to compare the identifier values of the records.

Two steps remain: We need to prepare a collection of courses from which the user can select and render the pick list. Let’s start by defining a Query component that fetches the courses. An alias is defined for the result set and scoped to the conversation to prevent redundant fetches (though request-scoped would also work here):

  <framework:entity-query name="coursesQuery" ejbql="select c from Course c"
    order="c.state asc, c.name asc"/>
  <factory name="courses" value="#{coursesQuery.resultList}"
    scope="conversation"/>

The courses context variable can now be used to back the <h:selectManyListbox> component. Here is the form fragment that renders the course pick list:

  <s:decorate id="coursesField" template="layout/display.xhtml">
    <ui:define name="label">Courses:<ui:define>
    <h:selectManyListbox id="courses" value="#{roundCriteria.courses}">
      <s:selectItems var="_course" value="#{courses}"
        label="#{_course.state} - #{_course.name}"/>
      <s:convertEntity/>
    </h:selectManyListbox>
  </s:decorate>

Seam also has parallel support for converting enum constants, activated by nesting the <s:convertEnum> tag within any form input. You can use it with a text field, in which case the user has to enter the enum constant, or a select menu, in which case the select items need to map to a collection of enum constants.

Thus far, you’ve seen restrictions that bind a property value to a query parameter through the use of a value expression. The restriction is enabled if that property value is non-null or non-empty. You may instead want to use a boolean property in the value expression to create a switched restriction.

Switched restrictions

To incorporate a dynamic parameter value in a restriction clause, you place decision logic in the value expression using the ternary operator. In this case, the criteria value is acting as a controller rather than the value of the parameter. This adds a shade of gray to the Query component’s black-and-white view of the restriction clauses.

As an example, we will add a checkbox to the criteria form that allows the user to toggle between all rounds and just the ones the user has played. First, a boolean property is added to the RoundCriteria class to capture the vanity flag:

  private boolean self = false;

  public boolean isSelf () { return this.self; }
  public void setSelf (boolean self) { this.self = self; }

Next, a restriction is added that checks the value of the self property and, if true, returns the currentGolfer context variable. If the value is false, or the currentGolfer is null because the user isn’t authenticated, the restriction is excluded:

  <value>g = #{roundCriteria.self ? currentGolfer : null}</value>

The search criteria appears in the form as a check box:

  <s:decorate id="selfField" template="layout/display.xhtml"
    rendered="#{currentGolfer != null}">
    <ui:define name="label">My rounds:</ui:define>
    <h:selectBooleanCheckbox id="self" value="#{roundCriteria.self}"/>
  </s:decorate>

Having seen this example, you should now recognize the added power that the EL gives you to make the restrictions contextual and to control whether the restriction is used through the use of a conditional.

When you’re all done, assuming you haven’t made any customizations, the round search page should look like figure 10.10.

Figure 10.10. The round search screen supported by a Query component

Before we close the books on query restrictions, I want to highlight one final scenario.

Just give me the numbers

Throughout this section, the focus has been on displaying a result set. But what if you want to use all of this great restriction functionality but in the end just retrieve a single number? Well, guess what? There’s really nothing to show. You simply change your JPQL/HQL to fetch a single result and then use the getSingleResult() method instead of getResultList() on the Query class. Let’s say the user wanted to get an average score of all rounds. You just define a new Query component, specify an aggregate query, and then find a place on the page to place the result:

  <framework:entity-query name="averageScore" scope="conversation"
    ejbql="select avg(r.totalScore) from Round r join r.golfer g">
    <framework:restrictions>
      <value>g = #{roundCriteria.self ? currentGolfer : null}</value>
    </framework:restrictions>
  </framework:entity-query>

The Query component is convenient because it allows you to get information up on the screen without having to hassle with boilerplate result set logic and unnecessary layers. It’s especially helpful when the feature requests come flying in. Storing the instance of Query in a long-term scope combined with its intelligence about when to execute a query can ensure minimal load on the database and thus good performance. I’ll leave it as an exercise for you to list the top rounds played at a course and for a golfer on the respective detail pages.

10.5. Summary

This chapter brought together everything you’ve learned about Seam. You used the component template classes in the Seam Application Framework to create screens for managing and listing golf rounds. The editor and detail screens were powered by a Home component, allowing the current golfer to create, read, update, and delete rounds. The editor screen used a long-running conversation to establish the tee set association and an application transaction to prevent changes from being made to the database prematurely. The listing screen was powered by a Query component, allowing the user to paginate, sort, and filter the rounds in the database. All of this functionality has traditionally been time consuming to implement, but with the Seam Application Framework component templates, you find yourself going for extra credit before the day is through.

The Seam Application Framework exemplifies how to create active domain models in Seam. The Home component, in particular, wraps an entity instance and the persistence manager into one cohesive unit, making it appear as though the entity is capable of reading and writing itself from the database. The Query component infuses behavior into a JPQL/HQL query and result set. From a design perspective, the tight coupling between the managed object and the persistence framework, present in the Active Record pattern and EJB 2, is avoided. You don’t have to use the template classes in the Seam Application Framework, but you may want to study them as a reference if you plan on implementing your own solution.

At this point you should be comfortable enough with Seam to start using it day to day. But there’s still a critical part missing from your knowledge, which I mentioned several times in this chapter: proper authentication of the user. In the next chapter, you finally get to fill out the stub authentication component installed by seam-gen and discover how to lock down the application at both the component and page levels.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.26.108