Spring to Java EE Migration, Part 2
By David Heffelfinger
CTO and ardent Java EE fan David Heffelfinger demonstrates how easy it is to develop the data layer of an application using Java EE, JPA, and the NetBeans IDE instead of the Spring Framework.
Downloads:
Introduction
In part I of this series, we began developing a complete Java Platform, Enterprise Edition (Java EE) rewrite of Spring's Pet Clinic sample application. We developed the persistence layer of our application using Java Persistence API (JPA) 2.0, and we saw how NetBeans can help us generate most of our persistence layer from an existing database schema.
We also analyzed the generated code, noting that it employs some advanced JPA features, including annotation attributes that can be used to regenerate tables, and it retains information such as the maximum allowed length and whether the field is nullable, as well as bean validation support.
In this part, we continue rewriting the Pet Clinic application, once again fully taking advantage of the Java EE tooling available in NetBeans. We develop Enterprise JavaBeans (EJB) 3.1 session beans that act as our Data Access Objects (DAOs), as well as JavaServer Faces (JSF) 2.0 managed beans and pages.
Note: The source code for the example described in this article can be downloaded here.
Generating Session Beans, Managed Beans, and JSF Pages
Now that we have our JPA entities, we need to come up with JSF pages, JSF managed beans, and some DAOs. We can generate all of them in one shot by using NetBeans' JSF Pages from Entity Classes wizard. We can access this wizard by going to File | New, selecting the Persistence category, and selecting the JSF Pages from Entity Classes file type, as shown in Figure 1.
Figure 1. JSF Pages from Entity Classes Wizard
We then need to select one or more existing JPA entities. We can select all of them by clicking the Add All button, as shown in Figure 2.
Figure 2. Select JPA Entities
Next, we need to select a package for the generated session beans and JSF managed beans, as shown in Figure 3.
Figure 3. Selecting a Package
After entering all the required information, NetBeans generates several JSF pages. These pages contain the user interface for all CRUD (Create, Read, Update, Delete) operations on the JPA entities in our project, as shown in Figure 4.
Figure 4. JSF Pages Generated by NetBeans
Additionally, NetBeans generates a number of Controllers (as in the “C” in MVC) in the form of JSF managed beans, as well as session beans acting as DAOs, plus some utility classes. See Figure 5.
Figure 5. Controllers Generated by NetBeans
At this point, we have a complete Java EE 6 application using JSF 2.0, EJB 3.1 session beans, and JPA 2.0 managed beans.
The Generated Application in Action
We can run the application by simply right-clicking the project and selecting Run, as shown in Figure 6.
Figure 6. Running the Application
At this point, the default browser is launched and our application can be seen in action, as shown in Figure 7.
Figure 7. The Running Application
As we can see, the generated index page has links to manage all the entities in our project.
By clicking any of the links, we can see a page displaying the corresponding database data, as shown in Figure 8.
Figure 8. Viewing the Corresponding Database Data
As we can see, the NetBeans wizard generates link a link to Create a new item, plus links to View (read), Edit (update), and Destroy (delete) each item. These links provide the CRUD functionality of our application.
Clicking the View link for any of the items allows us to see all the information for the corresponding item, as shown in Figure 9.
Figure 9. Viewing the Information for a Corresponding Item
In Figure 9, we can see the information for one of the pets. Notice that the primary key is shown. This information is not relevant to the user; therefore, we should remove it. Also, we see the primary keys for the corresponding type and owner, as opposed to a user-friendly textual representation. We can easily modify the generated code to make these changes (more on that later).
Clicking the Edit link, either from the view page or from the list page, allows us to modify an existing row in the database, as shown in Figure 10.
Figure 10. Modifying an Existing Row in the Database
The generated page could use some minor modifications. For example, the generated JPA entities are using JPA primary key generation; therefore, there is no need to have a field for the primary key. The option labels in the lists for Type and Owner are not exactly user friendly. These issues can be easily addressed by some minor modifications to the generated code (more on that later).
We can also add a new row to the database by clicking the Create New Pet link. (This user interface is nearly identical to the Edit page, so it is not shown here.) We can delete a row from the database by clicking the Destroy link.
Examining the Generated Code
The NetBeans wizard generates a lot of code for us including JSF pages, JSF managed beans, utility classes, and EJB 3.1 session beans.
The generated JSF pages are fairly straightforward, standard JSF pages, so we won't go into much detail. It is worth noting that the generated JSF pages take advantage of Facelets templating, making it easy to modify the layout across the board, if necessary. The generated template is named, appropriately enough, template.xhtml. See Listing 1.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui=" "
xmlns:h=" ">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title><ui:insert name="title">Default Title</ui:insert></title>
<h:outputStylesheet name="css/jsfcrud.css"/>
</h:head>
<h:body>
<h1>
<ui:insert name="title">Default Title</ui:insert>
</h1>
<p>
<ui:insert name="body">Default Body</ui:insert>
</p>
</h:body>
</html>
Listing 1. Generated Facelets Template
Not surprisingly, the generated template uses Facelets' <ui:insert> tag to mark areas that will change in all pages using the template.
We can see the corresponding <ui:define> tags, as shown in Listing 2, by opening almost any of the generated pages.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui=" "
xmlns:h=" "
xmlns:f=" ">
<ui:composition template="/template.xhtml">
<ui:define name="title">
<h:outputText value="#{bundle.ViewPetTitle}"></h:outputText>
</ui:define>
<ui:define name="body">
<!-- Content omitted for brevity -->
</ui:define>
</ui:composition>
</html>
Listing 2. “View Pet” JSF Page
It is also worth mentioning that the pages are styled using an automatically generated CSS style sheet. The generated JSF pages use the standard resource directories feature introduced in JSF 2.0. The CSS style sheet is included by the following markup in the Facelets template:
<h:outputStylesheet name="css/jsfcrud.css"/>
By default, the JSF implementation looks for a directory named resources, either under META-INF or at the root of the WAR file. Then it looks for a subdirectory called css under the resources directory and then for the specified CSS style sheet under that directory. We can see the directory structure by expanding the corresponding directories in the NetBeans project view, as shown in Figure 11.
Figure 11. Viewing the Directory Structure
In addition to pages, the wizard generates a number of JSF managed beans that serve as controllers in our application. See Figure 12.
Figure 12. Generated JSF Managed Beans
Most of the methods in the controllers are essentially pass-through methods that call the corresponding methods on the session bean EntityManager facades. However, there are a few that are worth looking into. See Listing 3.
package com.ensode.petclinicjavaee.managedbean;
import com.ensode.petclinicjavaee.entity.Pet;
/* Imports Omitted */
@ManagedBean(name = "petController")
@SessionScoped
public class PetController implements Serializable {
private Pet current;
private DataModel items = null;
@EJB
private com.ensode.petclinicjavaee.session.PetFacade ejbFacade;
private PaginationHelper pagination;
private int selectedItemIndex;
public PetController() {
}
/* getSelected() method omitted */
/* getFacade() method omitted */
public PaginationHelper getPagination() {
if (pagination == null) {
pagination = new PaginationHelper(10) {
@Override
public int getItemsCount() {
return getFacade().count();
}
@Override
public DataModel createPageDataModel() {
return new ListDataModel(getFacade().findRange(new int[]{
getPageFirstItem(), getPageFirstItem() +
getPageSize()}));
}
};
}
return pagination;
}
/* prepareList() method omitted */
public String prepareView() {
current = (Pet) getItems().getRowData();
selectedItemIndex = pagination.getPageFirstItem() + getItems().
getRowIndex();
return "View";
}
public String prepareCreate() {
current = new Pet();
selectedItemIndex = -1;
return "Create";
}
/* create method omitted */
public String prepareEdit() {
current = (Pet) getItems().getRowData();
selectedItemIndex = pagination.getPageFirstItem() + getItems().
getRowIndex();
return "Edit";
}
/* update method omitted */
/* destroy method omitted */
/* destroyAndView() method omitted */
/* performDestroy() method omitted */
/* updateCurrentItem() method omitted */
public DataModel getItems() {
if (items == null) {
items = getPagination().createPageDataModel();
}
return items;
}
private void recreateModel() {
items = null;
}
public String next() {
getPagination().nextPage();
recreateModel();
return "List";
}
public String previous() {
getPagination().previousPage();
recreateModel();
return "List";
}
public SelectItem[] getItemsAvailableSelectMany() {
return JsfUtil.getSelectItems(ejbFacade.findAll(), false);
}
public SelectItem[] getItemsAvailableSelectOne() {
return JsfUtil.getSelectItems(ejbFacade.findAll(), true);
}
@FacesConverter(forClass = Pet.class)
public static class PetControllerConverter implements Converter {
public Object getAsObject(FacesContext facesContext,
UIComponent component, String value) {
if (value == null || value.length() == 0) {
return null;
}
PetController controller = (PetController) facesContext.
getApplication().getELResolver().
getValue(facesContext.getELContext(), null, "petController");
return controller.ejbFacade.find(getKey(value));
}
java.lang.Integer getKey(String value) {
java.lang.Integer key;
key = Integer.valueOf(value);
return key;
}
String getStringKey(java.lang.Integer value) {
StringBuffer sb = new StringBuffer();
sb.append(value);
return sb.toString();
}
public String getAsString(FacesContext facesContext,
UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof Pet) {
Pet o = (Pet) object;
return getStringKey(o.getId());
} else {
throw new IllegalArgumentException("object " + object +
" is of type " + object.getClass().getName() +
"; expected type: " + PetController.class.getName());
}
}
}
}
Listing 3. Controller Methods
Notice that the JSF managed bean has an inner class annotated with the @FacesConverter annotation. This annotation was introduced in JSF 2.0 and it serves to mark the annotated class as a JSF converter.
JSF converters convert Java objects to strings and back. The getAsObject() method takes a string and converts it to the corresponding object. The generated implementation of this method takes the string value of the id property of our JPA entity, converts it to the corresponding integer value, and then queries the database via the generated session facade to obtain the corresponding JPA entity object.
The generated getAsString()simply returns the value of the id property of our JPA entity as a string.
There's another thing we should notice. The generated JSF managed bean uses a JSF DataModel implementation, as shown in Listing 4. The JSF DataModel interface makes it easier to manipulate the data displayed in a JSF data table. The generated JSF managed beans also make use of a utility class called PaginationHelper. This class provides pagination functionality, which is the ability to show a few items at a time on the page while providing “previous” and “next” links to the rest of the list.
package com.ensode.petclinicjavaee.managedbean.util;
import javax.faces.model.DataModel;
public abstract class PaginationHelper {
private int pageSize;
private int page;
public PaginationHelper(int pageSize) {
this.pageSize = pageSize;
}
public abstract int getItemsCount();
public abstract DataModel createPageDataModel();
public int getPageFirstItem() {
return page * pageSize;
}
public int getPageLastItem() {
int i = getPageFirstItem() + pageSize - 1;
int count = getItemsCount() - 1;
if (i > count) {
i = count;
}
if (i < 0) {
i = 0;
}
return i;
}
public boolean isHasNextPage() {
return (page + 1) * pageSize + 1 <= getItemsCount();
}
public void nextPage() {
if (isHasNextPage()) {
page++;
}
}
public boolean isHasPreviousPage() {
return page > 0;
}
public void previousPage() {
if (isHasPreviousPage()) {
page--;
}
}
public int getPageSize() {
return pageSize;
}
}
Listing 4. PaginationHelper.java
As we can see, PaginationHelper is an abstract class providing two abstract methods: getItemsCount() and createPageDataModel(). Each generated JSF managed bean provides an anonymous inner class extending PaginationHelper and providing concrete implementations of these two methods, as shown in Listing 5.
public PaginationHelper getPagination() {
if (pagination == null) {
pagination = new PaginationHelper(10) {
@Override
public int getItemsCount() {
return getFacade().count();
}
@Override
public DataModel createPageDataModel() {
return new ListDataModel(getFacade().findRange(new int[]{
getPageFirstItem(), getPageFirstItem() +
getPageSize()}));
}
};
}
return pagination;
}
Listing 5. Anonymous Inner Class that Extends PaginationHelper
The getItemsCount() implementation simply invokes the count() method on the generated EJB facade, which in turn returns the total number of rows in the database table that our JPA entity maps to. The createPageDataModel() method is a bit more interesting. It implements a lazy loading strategy, that is, only the items to be shown on the page are pulled from the database in real time. This is a memory saving measure, because we would usually have to pull all items from the database and keep them in memory as we paginate through the different items.
Now that we have seen the generated JSF code, let's take a look at the generated EJB 3.1 session beans. All the generated session beans extend a parent class called AbstractFacade.java, as shown in Listing 6.
package com.ensode.petclinicjavaee.session;
/* imports omitted */
public abstract class AbstractFacade {
private Class entityClass;
public AbstractFacade(Class entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
public List findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0]);
q.setFirstResult(range[0]);
return q.getResultList();
}
public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().
getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}
Listing 6. AbstractFacade.java
Notice that AbstractFacade implements the Facade design pattern, that is, it provides a simplified interface to the JPA EntityManager. The generated methods use the JPA 2.0 criteria API. This API is a major addition to the JPA specification, allowing us to dynamically build type-safe JPA queries.
Most of the work done by the generated session beans is performed by their parent class. All the child classes do is implement a constructor plus have an instance of EntityManager injected using the Java EE dependency injection mechanism, as shown in the Listing 7.
package com.ensode.petclinicjavaee.session;
/* imports omitted */
@Stateless
public class PetFacade extends AbstractFacade {
@PersistenceContext(unitName = "PetClinicJavaEEPU")
private EntityManager em;
protected EntityManager getEntityManager() {
return em;
}
public PetFacade() {
super(Pet.class);
}
}
Listing 7. Injection of EntityManager
As we can see, the constructor in the child class simply invokes the parent class constructor, passing the type of the JPA entity that this session bean manipulates. The parent class, in turn, uses generics to dynamically add the correct type to its entityClass instance variable.
An instance of EntityManager is injected by simply annotating it with the PersistenceContext annotation and passing the persistence unit name (as defined in persistence.xml) as the value of its unitName attribute. There is nothing out of the ordinary here.
Conclusion
In this article, we saw the generated application in action and we took a look “under the hood” to see what is going on. In the next installment of this series, we will modify the generated code to make it a bit more user friendly and we will compare the Java EE and Spring versions of the Pet Clinic application.
See Also
- Spring to Java EE Migration, Part 1
- Spring to Java EE Migration, Part 3
- Spring to Java EE Migration, Part 4
- Spring Framework: http://spring.io/
- NetBeans: http://netbeans.org/
- Java EE 6: http://www.oracle.com/java/technologies/java-ee-glance.html
About the Author
David Heffelfinger is the Chief Technology Officer of Ensode Technology, LLC, a software consulting firm based in the greater Washington D.C. area. He has been architecting, designing, and developing software professionally since 1995 and has been using Java as his primary programming language since 1996. He has worked on many large-scale projects for several clients including the U.S. Department of Homeland Security, Freddie Mac, Fannie Mae, and the U.S. Department of Defense. He has a master's degree in software engineering from Southern Methodist University.