Pagination of Data Sets in a Sample Application using JSF, Spring
2.0, and Java Persistence APIs on Glassfish
This Sample Store Catalog app demonstrates the usage of JavaServer
Faces, the new Java Persistence APIs, and Spring 2.0 to implement
pagination. I took this example
Pagination
of Data Sets in a Sample Application using JSF, Catalog Facade
Stateless Session, and Java Persistence APIs and modified it
slightly to use a Spring Bean instead of a EJB 3.0 Stateless Session
Bean. If you compare the two, you will see that the code is
almost the same, the main difference is the extra xml configuration for
Spring, and for me it wasn't easy to get the .xml
configuration right. It took looking at several articles, blogs,
examples, and some trial and error to get it working. (see the
references at the end).
download the SpringJPA sample application code
Note: I updated the Spring JSF integration part of this
application to use
the
org.springframework.web.jsf.DelegatingVariableResolver instead
of a
JSF Managed Bean ServiceLocator in order to get a
reference to the CatalogService Spring
Bean from the Spring
application context.
Explanation of the usage of JSF, Spring 2.0 , and Java Persistence
APIs in a sample Store Catalog Application
The image below shows the Catalog Listing page, which allows a user to
page through a list of items
in a store.
The List.jsp page uses a JSF
dataTable
component to display a list of
catalog items
The dataTable component is useful when you want to show a set of
results in a table. In a JavaServer Faces application, the
UIData
component
(the superclass of dataTable) supports binding to a collection of
data objects. It does the
work of iterating over each record in the data source. The HTML
dataTable
renderer
displays the data as an HTML table.
In the List.jsp web page the dataTable is defined as shown below:
(Note:
Red
colors
are for
Java EE
tags, annotations code,
Blue for
Spring
specific
and
Green
for
my code
or
variables)
Code Sample from: List.jsp
|
<h:dataTable value='#{item.items}' var='dataTableItem'
border="1"
cellpadding="2" cellspacing="0">
|
The
value attribute of a
dataTable
tag references the data to be included
in the table. The
var
attribute specifies a
name that is used by the components within the
dataTable
tag as an alias to the data referenced in the
value
attribute of
dataTable. In the
dataTable
tag from the
List.jsp
page, the
value attribute points to a list
of catalog items. The
var
attribute points
to a single item in that list. As the
UIData
component iterates through the list, each reference to
dataTableItem points to the current item in the
list.
The dataTable's
value
is bound to the
items
property
of the
item controller
class,
ItemController,
which is defined in the
faces-config.xml file:
| Code Sample from: faces-context.xml |
<managed-bean>
<managed-bean-name>item</managed-bean-name>
<managed-bean-class>
sessionpagination.ItemController
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>catalogService</property-name>
<value>#{catalogService}</value>
</managed-property>
</managed-bean>
|
This
ItemController ManagedBean
items
property is defined as shown below:
Code Sample from: ItemController.java |
public DataModel
getItems()
{
if (model==null ||
index != firstItem){
model=getNextItems();
}
return this.model;
}
public DataModel getNextItems()
{
model = new ListDataModel(catalogService.getItems(firstItem,batchSize));
index =
firstItem;
return this.model;
}
|
The
getItems()
method wraps a List of item objects, returned from the
catalogService,
in a
DataModel.
UIData,
the superclass of
dataTable,
supports data binding to a
collection of data objects represented by a
DataModel
instance. The data
collection underlying a DataModel instance is modeled as a collection
of row objects that can be accessed by a row index. The APIs
provide mechanisms to position to a specified row index, and to
retrieve an object that represents the data that corresponds to the
current row index.
The
Item
properties Name, Photo, and
price are
displayed with the
column
component:
Code Sample from: List.jsp
|
<h:column>
<f:facet name="header">
<h:outputText value="Price"/>
</f:facet>
<h:outputText value="#{dataTableItem.price}"/>
</h:column>
|
The
column
tags represent columns of data in a
UIData
component. While
the UIData component is iterating over the rows of data, it processes
the UIColumn component associated with each column tag for each row in
the table.
The
UIData component iterates through the list
of items
(
item.items)
and displays the
dataTableItem.price.
Each
time UIData iterates through the list of items, it renders one cell in
each
column.
The dataTable and column tags use
facet
to represent parts of the
table that are not repeated or updated. These include
headers,
footers,
and captions.
The recommended way to integrate Spring with JSF is to
configure the Spring
DelegatingVariableResolver
in the
faces-context.xml. The <application>
<variable-resolver>
elements in a faces-config.xml file allows a Faces-based application to
register a custom replacement class for the implementation of the
standard Faces VariableResolver
implementation. The Spring DelegatingVariableResolver
first delegates to the original resolver of the underlying JSF
implementation, then to the Spring root
WebApplicationContext. This
allows you to configure Spring Beans as managed properties of your JSF
Managed Beans. For example, below the catalogService
Spring
Bean is configured as a managed property of the
ItemController
JSF ManagedBean:
| Code Sample from: faces-context.xml |
<application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
</application>
<managed-bean>
<managed-bean-name>item</managed-bean-name>
<managed-bean-class>
sessionpagination.ItemController
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>catalogService</property-name>
<value>#{catalogService}</value>
</managed-property>
</managed-bean>
|
The
catalogService,
and its implementation
CatalogDAO,
is defined as a
Spring
bean in the Spring configuration resource file
/WEB-INF/applicationContext.xml
:
| Code Sample from: applicationContext.xml
|
<bean id="catalogService" class="service.CatalogDAO"/>
<bean name="itemController" class="sessionpagination.ItemController">
<property
name="catalogService">
<ref bean="catalogService"/>
</property>
</bean>
</beans>
|
<property
name="catalogService"> refers to the
ItemController setCatalogService
method.
The Spring root
WebApplicationContext will inject the
catalogService
Spring Bean
into the
catalogService
property
of the
ItemController
JSF
ManagedBean :
Code Sample from: ItemController.java |
public class ItemController
{
private CatalogService catalogService ;
public void setCatalogService(CatalogService
catalogService) {
this.catalogService =
catalogService;
}
|
For more information on using JSF with Spring, see
Spring
- Java/J2EE Application Framework Integrating with JavaServer Faces
.
Using the Java Persistence API (JPA) with Spring 2.0
The Spring bean
CatalogDAO
uses the Java
Persistence API
EntityManager
Query object to return a list of items.
The
CatalogDAO annotates the field
private EntityManager em;
with
@PersistenceContext
, which causes an entity manager to be
injected.
(note that using the
@PersistenceContext annotation is the same
way an
Entity Manager is injected for a
EJB
3.0 Session Bean.)
| Code Sample from: CatalogDAO.java |
@Repository
@Transactional
public class CatalogDAO
implements CatalogService
{
@PersistenceContext(unitName="PetCatalogPu")
private EntityManager
em;
public List<Item> getItems(int
firstItem,int batchSize) {
Query q = em.createQuery("select
object(o)
from Item as o");
q.setMaxResults(batchSize);
q.setFirstResult(firstItem);
List<Item> items=
q.getResultList();
return
items;
}
|
The Java Persistence Query
APIs are used to create and execute queries that can return a
list of results. The JPA Query interface provides
support for pagination via the setFirstResult() and setMaxResults()
methods: q.setMaxResults(int maxResult)
sets the maximum number of results to retrieve. q.setFirstResult(int startPosition)
sets the position of the first result to retrieve.
In the code below, we show the
Item
entity class which maps to the ITEM table that stores the
item instances. This is a
typical Java Persistence entity object. There are two requirements for
an entity:
- annotating the class with an @Entity
annotation.
- annotating the primary key identifier with @Id
Because the fields name, description.... are basic mappings from the
object fields to columns of the same name in the database table, they
don't have to be annotated. The O/R relationships with
Address
and
Product
are also annotated. For more information on
defining JPA entities see
Pro
EJB 3: Java Persistence API book.
| Code Sample from: Item.java |
@Entity
public class Item
implements java.io.Serializable {
@Id
private String itemID;
private String name;
private String description;
private String imageurl;
private String imagethumburl;
private BigDecimal price;
@OneToOne(cascade={CascadeType.PERSIST})
private Address
address;
@ManyToOne
private Product
product;
public Item() { }
public String getItemID() {
return itemID;
}
public void setItemID(String itemID) {
this.itemID = itemID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String
description) {
this.description = description;
}
public BigDecimal getPrice() {
return
price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public void setAddress(Address address) {
this.address = address;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
...
}
|
The
ItemController
ManagedBean pages through the list of
Items
by
maintaining the firstItem and batchSize attributes and passing these as
parameters to the
CatalogService getItems(firstItem,
batchSize) method.
The
ItemController's
<managed-bean-scope> is
defined as
session,
a JSF Managedbean
with session scope will be stored in the session meaning that the
bean's properties will stay alive for the life of the Http Session.
The
ItemController
ItemCount
property is used to get and display
the number of Catolog
items
in the data base on the
List.jsp page:
| Code Sample from: List.jsp |
<h:outputText
value="Item
#{item.firstItem +
1}..#{item.lastItem} of
#{item.itemCount}"/>
|
This
ItemController
ItemCount property
is defined as shown below:
| Code Sample from: ItemController.java |
public int getItemCount()
{
int count = catalogService.getItemsCount();
return
count;
}
|
The
ItemController
getItemCount()
method calls the
CatalogService
interface to get
the count of the list of items. The
CatalogDAO
Spring bean
getItemCount()
method uses the JPA Query interface to get the count of
all items in the database item table:
Code Sample from: CatalogDAO.java |
public class CatalogDAO
implements CatalogService
{
. . .
public int getItemCount()
{
Query q =
entityManager.createQuery("select
count(o) from Item as
o");
int count =
((Long)q.getSingleResult()).intValue();
return count;
}
|
A JSF
commandLink
is used to provide a link to click on to
display the next page of items. The
commandLink
tag represents an HTML hyperlink and is rendered as an HTML
<a> element. The
commandLink
tag is used to submit an
action event
to the application.
| Code Sample from: List.jsp |
<h:commandLink
action="#{item.next}"
value="Next
#{item.batchSize}"
rendered="#{item.lastItem + item.batchSize <=
item.itemCount}"/>
|
This
commandLink action
attribute
references the
ItemController
backing bean
next()
method which calculates
the
next page's first row number and returns a logical outcome
String, which causes the
List.jsp page
to display the next page's
list .
The
ItemController
next
method is defined as shown below:
Code Sample from: ItemController.java |
public String next() {
if (firstItem + batchSize
< getItemCount()) {
firstItem += batchSize;
}
return "item_list";
}
|
The JavaServer Faces
NavigationHandler
matches the logical outcome,
item_list
against the navigation rules in the application configuration resource
file
faces-config.xml
to determine which page to access next. In this case, the
JavaServer Faces implementation loads the
List.jsp
page after this method returns.
| Code Sample from: faces-config.xml |
<navigation-rule>
<navigation-case>
<from-outcome>item_list</from-outcome>
<to-view-id>/item/List.jsp</to-view-id>
</navigation-case>
</navigation-rule>
|
A JSF
commandLink is
used to provide a link to click on to
display the previous page of items. This
commandLink
action attribute
references the
ItemController
prev() method
that
calculates the
previous page's first row number and returns a logical outcome
String, which causes the List page to display the previous page of
items :
| Code Sample from: List.jsp |
<h:commandLink
action="#{item.prev}"
value="Previous
#{item.batchSize}"
rendered="#{item.firstItem >=item.batchSize}"/>
|
This
ItemController
prev()
method is defined as shown
below:
Code Sample from: ItemController.java |
public String prev()
{
firstItem -= batchSize;
if (firstItem < 0) {
firstItem = 0;
}
return "item_list";
}
|
A JSF
ommandLink
is used to provide a link to click on to
display a page with the
item details. This
commandLink
action attribute
references The
ItemController
detailSetup()
method:
| Code Sample from: List.jsp |
<h:column>
<f:facet name="header">
<h:outputText
value="Name"/>
</f:facet>
<h:commandLink action="#{item.detailSetup}"
value="#{dataTableItem.name}"/>
</h:column>
|
The
ItemController
detailSetup()
method gets the
item
data from the
current row of the
dataModel,
and returns a string which causes the
Detail.jsp
page to display
the item details :
Code Sample from: ItemController.java
|
public String detailSetup()
{
item = (Item)
model.getRowData();
return "item_detail";
}
|
The JavaServer Faces
NavigationHandler
matches the logical outcome,
item_detail
against the navigation rules in the application configuration resource
file to determine which page to access next. In this case, the
JavaServer Faces implementation loads the
Detail.jsp
page after this method returns.
| Code Sample from: faces-config.xml |
<navigation-rule>
<navigation-case>
<from-outcome>item_detail</from-outcome>
<to-view-id>/item/Detail.jsp</to-view-id>
</navigation-case>
</navigation-rule>
|
The Detail.jsp uses the
outputText
component to display the
ItemController
ManagedBean's
item
properties:
| Code Sample from: detail.jsp |
<h:outputText
value="#{item.item.name}"
title="Name" />
<h:outputText value="#{item.item.description}"
title="Description"/>
<h:graphicImage url="#{item.item.imageurl}" title="Imageurl" />
<h:outputText value="#{item.item.price}" title="Price" />
<h:outputText value="#{item.item.address.city}" title="Address" />
<h:outputText value="#{item.item.contactinfo.email}"
title="Address"/>
|
Conclusion
This concludes the sample application which demonstrates how to work
with the
JSF
dataTable and
DataModel
to page through a list
of
Item
Entities
which are retrieved using the
Catalog
methods which use the
Java
Persistence APIs with
Spring 2.0.
Setting up the database tables for the
sample application:
- edit the properties in the SpringJPA\setup\javadb.properties
file, then run the
ant script in the directory SpringJPA\setup, or just run the sql in the
directory
SpringJPA\setup\sql\javadb with whatever tool you have.
Configuration of the Application
for Spring 2.0, JSF, JPA, running on Glassfish
To set up glassfish and the netbeans project for Spring, I
modified the steps from
Spring and
Hibernate in GlassFish :
Setting Things Up:
- download and extract
Spring ( I downloaded 2.1).
- Download
and install NetBeans 5.5.1
- Download
and install GlassFish V1, following the instructions on the download
page. Alternatively you can use Sun Java System Application Server PE
9, Sun's GlassFish distribution.
- Add the Glassfish server to Netbeans:
- Start NetBeans and switch to the Runtime window (Ctrl+5).
- Right-click the Servers node and select Add Server.
- select Sun Java System Application Server (Sun's binary
distribution of GlassFish).
- Browse to the location where you installed GlassFish and
select Finish.
Create the Spring Library in Netbeans:
- Open the Netbeans Library Manager (Tools menu) and create a new
library
called Spring.
- Add the following jars to the Classpath:
- dist/spring.jar, dist/weaving/spring-aspects.jar,
lib/jakarta-commons/commons-logging.jar, lib/log4j/log4j-1.2.9.jar .
- Set the Sources to the Spring src
directory.
- Set the Javadoc to the Spring docs\api
directory.
This library is now available for use by any project.
Open and Run the SpringJPA Project
Download
The SpringJPA application. Unzip the code. From Netbeans do Open
Project
and select the Netbeans project SpringJPA
from the unzipped directory. The SpringJPA application is a
NetBeans Enterprise Application
Project, which is actually comprised of 2 projects: SpringJPA and
SpringJPA-war. SpringJPA-war is a Java EE Module
of the SpringJPA project. SpringJPA-war generates the war file
and SpringJPA generates the ear file which contains the war.
Open the SpringJPA project. You will
see this dialog when you open the project, because the
Enterprise Application Project stores the absolute location to its
J2EE Modules, you need to configure the location for the SpringJPA-war.
- Click Close. The SpringJPA project will be in bold red.
- Right click the project and select Resolve Reference Problems
from the context menu.
- Use the Resolve Reference Problems dialog to map the
SpringJPA-war
module to its
project, which you'll find is a subdirectory beneath the SpringJPA
directory .
- After the references are resolved, right-click the SpringJPA
project
and select Open Required Projects (now that the dependencies are
correct, the SpringJPA-war project will always open with the SpringJPA
project).
- There are still additional references problems with the web
module, since it references the
Spring jar files that are needed to build the project.
- add the new Spring
library to the SpringJPA-war...
in the project window under SpringJPA-war:
right click on Librairies, Add Library
Select the Spring Library (that you created above) from the list, then
click Add Library . This will add the Spring jar files to your
SpringJPA-war project.
- All references should now be resolved. Right-click the
SpringJPA project and select clean and build project.
- Press F6 to test run the
project. NetBeans will build, package, deploy and launch the
application.
Creating
your own Netbeans with Spring & Glassfish Project :
- If you want to create your own application, Create a new
Netbeans Enterprise Application:
- In Netbeans select File New Project, then select
Enterprise ..Enterprise Application, on the New Enterprise Application
Window, for Server select Sun Java System Applicaton server, Java
EE 5 Version, and Select the Create Web Application Module as shown
below:
- change your Netbeans project properties to add the new Spring
library...
in the project window under your application's war:
right click on Librairies, Add Library
Select Spring Library from the list, then click Add Library .
- to Generate Entity classes from the database tables: In the
project window, right click on the war, select new File..Persistence
Entity classes from database. Click next, then select your
datasource, tables and create your persistence unit. For more info on
how to do this try out the following Hands On Lab: Java EE 5,
EJB 3.0, Java Persistence API (JPA)
- For Spring configuration add the applicationConfiguration.xml
and modify the web.xml
and faces-config.xml
as described below.
Configuration of
the XML files for Spring 2.0, JSF, and JPA, running on Glassfish
- Add the file /WEB-INF/applicationContext.xml
to the war WEB-INF
directory. This file is where you define your
Spring
service beans, and resources. Below is the applicationContext.xml for
the sample app. For more information about configuring the Spring
applicationContext.xml for JPA see this article: Using the Java
Persistence API (JPA) with Spring 2.0
| Code Sample from: applicationContext.xml
|
<?xml
version="1.0"
encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="catalogService"
class="service.CatalogDAO"/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property
name="dataSource" ref="dataSource"/>
<property
name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.glassfish.GlassFishLoadTimeWeaver"/>
</property>
<property
name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="databasePlatform"
value="oracle.toplink.essentials.platform.database.DerbyPlatform"
/>
</bean>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property
name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"
/>
<property name="url"
value="jdbc:derby://localhost:1527/pet-catalog"
/>
<property name="username"
value="app" />
<property name="password"
value="app" />
</bean>
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property
name="entityManagerFactory" ref="entityManagerFactory"/>
<property
name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven />
<bean name="itemController"
class="sessionpagination.ItemController"
scope="session">
<property
name="catalogService">
<ref bean="catalogService"/>
</property>
</bean>
</beans>
|
| Code Sample from: web.xml
|
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
|
- Add the
DelegatingVariableResolver and
any Spring beans (i.e. catalogService)
referenced as JSF
Managed Bean (i.e. ItemController) properties
to the faces-config.xml :
| Code Sample from: faces-config.xml
|
<application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
</application>
<managed-bean>
<managed-bean-name>item</managed-bean-name>
<managed-bean-class>
sessionpagination.ItemController
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>catalogService</property-name>
<value>#{catalogService}</value>
</managed-property>
</managed-bean>
|
References
Thank You.