Skip to main content

Extending the NetBeans Tutorial JSF-JPA-Hibernate Application, Part 1 - Co-ordinating JSF View Parameter Passing to Managed Bean

Posted by maxpoon on June 13, 2007 at 12:47 PM PDT

Background

The NetBeans tutorial "Using Hibernate With the Java Persistence API" nicely demonstrates, by using the NetBeans IDE, easy construction of :

  • Java Persistence API (JPA) entity classes from given database schema (using NetBeans IDE bundled 'sample' database)
  • JavaServer Faces (JSF) CRUD application (on the 'sample' database) using JPA and entity classes generated above

The follow-up tutorial "NetBeans Wiki - UsingHibernateWithJPA" further shows usage of JPA as well as Hibernate-specific facilities, including :

While the above-mentioned two tutorials demonstrate ease of JSF-JPA-Hibernate application construction with usage of JPA and Hibernate facilities in the persistence-tier, it is interesting to explore also on the flexibility and power of JSF in the web-tier.

As the resulting created application provides individual CRUD functions to the actually mutually-associated entity classes, it makes sense to see how, using JSF, we can, easily in the two steps described, extend the application to add new web query views from the CRUD view of one entity class to list the links to CRUD views of its associated entity classes with re-use of existing individual CRUD views as much as possible.  This is an example of co-ordinating query views based on parameter passing from JSF View to Managed Bean.  Adding a 'new view' to list all Products associated with a selected ProductCode via the ProductCode-Product one-to-many association is used here.

Extending The Sample Application

Based on the "Using Hibernate With the Java Persistence API" tutorial (but choosing the sample database tables ProductCode, Product, and Manufacturer instead of Customer and DiscountCode), our JSF-JPA-Hibernate sample application (let's call it 'SimpleJpaHibernateApp') can be created.  (Please refer to the original tutorial for other details of software needed and environment set-up).

Figure 1 - 'Sample' Database Tables to be used by SimpleJpaHibernateApp
SimpleJpaHibernateApp-DBSchema.png

At this time, our SimpleJpaHibernateApp created provides individual CRUD functions to the three actually associated entity classes (e.g. both ProductCode-Product and Manufacturer-Product having one-to-many associations).

Here, we shall pick the ProductCode->Product navigation (i.e. enquiring on all Products for CRUD, given a selected ProductCode) since there are 6 ProductCodes and 30 Products (so it's in average 1:5 ratio) from the out-of-the-box NetBeans 'sample' database.

In this case, our application (and the targeted new query function) should be as follows, starting with its home page with the HTML links to :

  • "List of ProductCode" - listing all the ProductCodes records (in the 'sample' database)
  • "List of Product" - listing all the Products records
  • "List of Manufacturer" - listing all the Manufacturer records

 

Figure 2 - SimpleJpaHibernateApp Home Page

SimpleJpaHibernateApp-Home0.png

Let's start the query navigation with ProductCode by clicking on the "List of ProductCode" link to lists all the ProductCodes :

Figure 3 - "Listing ProductCodes" Page

SimpleJpaHibernateApp-ProdCodeList0.png

Then, click on one of the ProductCodes, e.g. "SW" in ProdCode column, the "Detail of ProdCode" for ProdCode "SW" will be displayed :

Figure 4 - "Detail of ProductCode" Page

SimpleJpaHibernateApp-ProdCodeDetail0.png

Now, it would be nice if we can add a HTML link "List of Product with this ProductCode" (highlight in red box) to display all the Products of this selected ProductCode with ProdCode="SW" :

Figure 5 - New (Desired) "Detail of ProductCode" Page

SimpleJpaHibernateApp-ProdCodeDetail1.png

So that clicking on "List of Product with this ProductCode" will display :

Figure 6 - New (Desired) "Listing Products" Page

SimpleJpaHibernateApp-SelectedProdList-SW.png

This can be done easily in JSF

An quite obvious way is to get the selected ProdCode in the "Detail of ProductCode" page, stored in JSF EL :

  • #{productCode.productCode.prodCode}

as shown in the Coding Listing 1, for reference by the subsequently invoked action

  • <h:commandLink action="productCode_list" value="Show All ProductCode"/>

which is simpleJpaHibernateApp/web/product/List.jsp (shown in the following Code Listing 2) as defined in faces-config.xml.

Code Listing 1 - Original "Detail of ProductCode" Page -
Location: simpleJpaHibernateApp/web/productCode/Detail.jsp
<%@page contentType="text/html"%>

<%@page pageEncoding="UTF-8"%>

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

<%@taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

<html>

  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    <title>SimpleJpaHibernateApp - Detail of ProductCode</title>

  </head>

  <body>

    <f:view>

      <h:messages errorStyle="color: red" infoStyle="color: green"
                  layout="table"/>


      <h1>Detail of productCode</h1>

      <h:form>

        <h:panelGrid columns="2">

          <h:outputText value="ProdCode:"/>

          <h:outputText title="ProdCode"
           
value="#{productCode.productCode.prodCode}"/>

          <h:outputText value="DiscountCode:"/>

          <h:outputText title="DiscountCode"
            value="#{productCode.productCode.discountCode}"
/>

          <h:outputText value="Description:"/>

          <h:outputText title="Description"
           
value="#{productCode.productCode.description}"/>

        </h:panelGrid>

        <h:commandLink action="productCode_edit" value="Edit" />

        <br>

        <h:commandLink action="productCode_list"

          value="Show All ProductCode"/>

        <br>

        <a href="/SimpleJpaHibernateApp/index.jsp">Back to Home Page</a>

      </h:form>

    </f:view>

  </body>

</html>

Now, let's look at how the subsequent "List of Product" works :

Code Listing 2 - "List of Product" Page -
Location: simpleJpaHibernateApp/web/product/List.jsp
<%@page contentType="text/html"%>

<%@page pageEncoding="UTF-8"%>

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

<%@taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

<html>
  ...


  <body>

    <f:view>

      <h:messages errorStyle="color: red" infoStyle="color: green"
        layout="table"/>


      <h1>Listing Products</h1>


      <h:form>

      <h:commandLink action="#{product.createSetup}"
        value="New Product"/>


      <br>

      <a href="/SimpleJpaHibernateApp/index.jsp">Back to Home Page</a>

      <br>

      <h:outputText value=

        "Item #{product.firstItem + 1}..#{product.lastItem} of #{product.itemCount}"/>
       


      <h:commandLink action="#{product.prev}"
        value="Previous #{product.batchSize}"
        rendered="#{product.firstItem >= product.batchSize}"/>
       


      <h:commandLink action="#{product.next}"
        value="Next #{product.batchSize}"
        rendered="#{product.lastItem + product.batchSize <=
        product.itemCount}"/>
       


      <h:commandLink action="#{product.next}"

        value="Remaining #{product.itemCount - product.lastItem}"

        rendered="#{product.lastItem < product.itemCount &&

        product.lastItem + product.batchSize > product.itemCount}"/>



      <h:dataTable value='#{product.products}' var='item' border="1"
          cellpadding="2" cellspacing="0">


      ...

      </h:dataTable>

      </h:form>

    </f:view>

  </body>

</html>

It can be observed that Products and ItemCount are retrieved from the JSF Expressions :

  •  #{product.products} which binds to the getProduct() method
  •  #{product.itemCount} which binds to the getItemCount() method

of the managed bean simpleJpaHibernate.controller.ProductController.
[Note: Examing ProductController.java shows that the other #{product.firstItem} and  #{product.lastItem} actually derive from #{product.itemCount}.]

Hence, if we can have these two methods be aware of the selected ProdCode to :

  • get only Products with the selected Prodcode
  • count only Products with the selected ProdCode

respectively, we shall be able to re-use simpleJpaHibernateApp/web/product/List.jsp to display a table of Products with the selected ProdCode.

Step 1 - Add New JSF CommandLink in "Detail of ProducCode" Page to Pass the Selected ProdCode to New Bean Method in ProductController

Using the added <h:commandLink ...> (highlighted below), the selected ProdCode in #{productCode.productCode.prodCode} can be passed as JSF FacesContext parameter selectedProdCode from simpleJpaHibernateApp/web/productCode/Detail.jsp to simpleJpaHibernate.controller.ProductController :

Code Listing 3 - New <h:commandLink...> added to "Detail of ProductCode" page -
simpleJpaHibernateApp/web/productCode/Detail.jsp
  ....
  <h:commandLink action="productCode_list"


                  value="Show All ProductCode"/>

  <br>

  <h:commandLink action="#{product.setSelectedProdCodeDisplayProducts}"

      value="List of Product with this ProductCode">

    <f:param name="selectedProdCode"

        value="#{productCode.productCode.prodCode}"/>

  </h:commandLink>

  <br>
  <a href="/SimpleJpaHibernateApp/index.jsp">Back to Home Page</a>
  ....

 

Step 2 - Add New Bean Property and Method, and Modify Related Bean Methods in ProductController

The 'passed-in' FacesContext selectedProdCode parameter is to be retreived by ProductController with the following new method setSelectedProdCodeDisplayProducts()

Code Listing 4 - New bean methods setSelectedProdCode(String selectedProdCode) and
setSelectedProdCodeDisplayProducts() of simpleJpaHibernateApp.controller.ProductController

  /**
   * Setter for selectedProdCode property
   * @param selectedProdCode String PROD_CODE column value of the selected
   *                         ProducCode e.g. in productCode/Detail.jsp
   */
  public void setSelectedProdCode(String selectedProdCode) {
     this.selectedProdCode = selectedProdCode;
  }

  /**

   * setSelectedProdCodeDisplayProducts() :

   * <ol>

   * <li>Get 'selectedProdCode' parameter from FacesContext</li>

   * <li>Set bean property 'selectedProdCode' with the parameter value</li>

   * <li>Return "product_list" to invoke product/List.jsp as defined in

   *     faces-config.xml to display Products with this selectedProdCode</li>

   * </ol>

   */

  public String setSelectedProdCodeDisplayProducts() {


     String _selectedProdCode =
        (String) FacesContext.getCurrentInstance()


           .getExternalContext().getRequestParameterMap()
           .get("selectedProdCode");




     setSelectedProdCode(_selectedProdCode);


     return "product_list";

  }

Also, the getProducts() and getItemCount() methods are required to be modified to get the 'selectedProdCode-aware' query behaviour :

Code Listing 5 - Modified managed bean methods getProducts() and getItemCount() of simpleJpaHibernateApp.controller.ProductController

  /**
   * Retrieve Products and return as ListDataModel according to:
   * <ol>
   *   <li>ProductCode selected already
   *     <code>
   *     (selectedProdCode != null  && selectedProdCode.length() != 0)
   *     </code>
   *     => retrieve Products with the selected ProductCode
   *   </li>
   *   <li>ProductCode not selected (otherwise)
   *     => retrieve all Products
   *   </li>
   * </ol>
   */


  public DataModel getProducts() {

     EntityManager em = getEntityManager();

     try{

        Query q;

        if (selectedProdCode != null && selectedProdCode.length() != 0) {

           q = em.createQuery("select object(o) from Product as o " +

               "where o.productCode.prodCode = :selectedProdCode")

               .setParameter("selectedProdCode", selectedProdCode);

        } else {

           q = em.createQuery("select object(o) from Product as o");

        }

        q.setMaxResults(batchSize);

        q.setFirstResult(firstItem);

        model = new ListDataModel(q.getResultList());

        return model;

     } finally {

        em.close();

     }

  }

  /**
   * Count no. of Products items according to:
   * <ol>
   *   <li>ProductCode selected already
   *     <code>
   *     (selectedProdCode != null  && selectedProdCode.length() != 0)
   *     </code>
   *     => count number of Products with the selected ProductCode
   *   </li>
   *   <li>ProductCode not selected (otherwise)
   *     => count number of all Products
   *   </li>
   * </ol>
   */
  public int getItemCount() {
     EntityManager em = getEntityManager();
     try{
        Query q;
        if (selectedProdCode != null && selectedProdCode.length() != 0) {
           q = em.createQuery("select count(o) from Product as o " +

               "where o.productCode.prodCode = :selectedProdCode")

               .setParameter("selectedProdCode", selectedProdCode);
        } else {
           q = em.createQuery("select count(o) from Product as o");
        }
        int count = ((Long) q.getSingleResult()).intValue();
        return count;
     } finally {
        em.close();
     }
  }

 

Some Final Steps - Related to index.jsp

Basically, the addition of new query based on the selected ProdCode (as criteria) passed from previous "Detail of ProductCode" JSF page / view (based also on modification/re-use of existing "Listing Products" view) is completed.

However, also due to the introduction of new query behaviour to the "Listing of Products" view, there are a few subsequent amendments to the application needed.

This includes the requirement to reset the newly introduced bean property selectedProdCode of ProductController in case of invocation from the application home page, to display all available Products in the database as expected.  Again, this can be done by the very same JSF CommandLink, this time with "" for the selectProdCode parameter :

  • <h:commandLink value="List of Product"
        action="#{product.setSelectedProdCodeDisplayProducts}">
      <f:param name="selectedProdCode" value=""/>
    </h:commandLink>
Code Listing 6 - Modified index.jsp
<%@page contentType="text/html"%>

<%@page pageEncoding="UTF-8"%>

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

<%@taglib uri="http://java.sun.com/jsf/html" prefix="h" %>



<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">



<%--

  index.jsp -  Modified from index.jsp_orig to use JSF <h:commandLink ...>

  to clear any previously set ProductController#selectedProdCode for listing

  Products via 'product_list' action

  @author Max Poon (maxpoon@dev.java.net)

--%>



<html>

    <head>

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

        <title>Simple JPA Hibernate Application</title>

    </head>

    <body>

        <f:view>

            <h:messages errorStyle="color: red" infoStyle="color: green"
                    layout="table"/>


            <h1>Simple JPA Hibernate Application</h1>

            <h:form>

                <br/>

                <h:commandLink value="List of ProductCode"

                        action="productCode_list"/>

                <br/>

                <h:commandLink value="List of Product"

                        action="#{product.setSelectedProdCodeDisplayProducts}">

                    <f:param name="selectedProdCode" value=""/>

                </h:commandLink>

                <br/>

                <h:commandLink value="List of Manufacturer"

                        action="manufacturer_list"/>

            </h:form>

        </f:view>

    </body>

</html>

This requires configuration / invocation of the new index.jsp as JSF View via the application's web.xml servlet-mapping  :

Code Listing 7 - Example new servlet-mapping required in web.xml
    ....
    <servlet-mapping>


        <servlet-name>Faces Servlet</servlet-name>

        <url-pattern>/faces/*</url-pattern>

    </servlet-mapping>

    <servlet-mapping>

        <servlet-name>Faces Servlet</servlet-name>

        <url-pattern>*.jsf</url-pattern>

    </servlet-mapping>
    ....

with all correspondingly links to the SimpleJpaHibernateApp home page modified from index.jsp to index.jsf, i.e. :

Code Listing 8 - Modified href link to "Home Page" in all referencing JSP's
    ....
    <%-- Modified link


         from /SimpleJpaHibernateApp/index.jsp

           to /SimpleJpaHibernateApp/index.jsf

    --%>

    <a href="/SimpleJpaHibernateApp/index.jsf">Back to Home Page</a>

    ....

For completeness' sake in this case, we should also have an index.html (suggested to be set as welcome-file in web.xml) so that requesting :

  • http://host:port/simpleJpaHibernateApp/

will be automatically redirected to the correct SimpleJpaHibernateApp home page

  • http://host:port/simpleJpaHibernateApp/index.jsf.

 

Done, Ready for Compilation, Deployment and Testing

So, SimpleJpaHibernateApp is now ready for compilation, deployment and testing, e.g. by clicking "List Product with this ProductCode" from "Detail of ProductCode" page with ProductCode as, e.g. "HW", and you should get :

Figure 6 - "Listing Products" Page for "HW" ProductCode

SimpleJpaHibernateApp-SelectedProdList-HW.png

while clicking "List of Product" on the Home Page, you should get all the available Products for ProdCode "SW", "HW", etc.

Figure 7 - "Listing Products" Page for All ProductCodes, i.e. All Available Products

SimpleJpaHibernateApp-SelectedProdList-ALL.png

If you can get the above query views working, congratulations!  Your SimpleJpaHibernate should be working fine, and ready for your further exploration!

Coming Next

In coming (hopefully soon) "Extending the NetBeans Tutorial JSF-JPA-Hibernate Application, Part 2", I would like to share on enabling JMX monitoring on Hibernate 3.2.x, and on the latest Ehcache 1.3.0 with full JMX support and detail per-cache monitoring statistics (yes, it's great!) - using our SimpleJpaHibernateApp as example (thanks to Ehcache's creator and maintainer Greg Luck for his helping me get the JMX monitoring on Ehcache working for the SimpleJpaHibernateApp, when we met at JavaOne May this year)

Stay tuned!

Related Topics >>