Spring to Java EE Migration, Part 4
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
This is the fourth and final part in a series of articles explaining how to migrate from the Spring Framework to Java EE.
In the first two parts(Spring to Java EE Migration, Part 1 and Spring to Java EE Migration, Part 2), we wrote a version of the Pet Clinic sample application bundled with the Spring Framework, using Java EE (JavaServer Faces [JSF], the JSF Facelets feature, Enterprise JavaBeans [EJB] 3.1, and Java Persistence API [JPA]) instead of Spring. Along the way, we showed how the powerful tooling included with NetBeans enables us to develop a complete Java EE application in record time. Then we analyzed the generated code, pointing out several nice Java EE features the wizard takes advantage of, such as Facelets templating; JSF data models; custom JSF converters; JPA annotation attributes that enable us to fully regenerate database schemas, preserving information such as maximum length and nullable and non-nullable values; and more.
In part 3, we tweaked the generated Java EE application to make it more user-friendly. We modified some generated labels to have more-understandable names, and we replaced some surrogate primary keys shown in the user interface with values that are meaningful to the user. In part 3, we also compared the number of dependencies of the Spring and Java EE versions of the application, the number of lines of configuration required for each, and the number of lines of code and markup required for each version, keeping in mind that the Java EE version of the application we have been developing in this series is not a direct port of the Spring version and actually has more functionality than the version bundled with the Spring Framework.
Part 4 compares equivalent functionality in Java EE and Spring, covering topics such as MVC design pattern implementation, data access, transaction management, and dependency injection.
Model-View-Controller Implementation
Using the model-view-controller (MVC) design pattern is the de facto standard way of laying out enterprise Java applications, no matter whether Java EE or Spring is used. Both frameworks provide ways to help application developers develop applications with this design pattern.
The model in the MVC pattern is usually a data object that gets passed around between the application layers.
MVC Models in Spring
With Spring, the model is a Plain Old Java Object (POJO) that is added to an implementation of the Spring-specific model
interface via its addAttribute()
method, as seen (among other places) in the setupForm() method of AddOwnerForm.java
in the Spring version of the application.
@RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
Owner owner = new Owner();
model.addAttribute(owner);
return "ownerForm";
}
Once this is done, the model class and attributes can be accessed from the view via the JSP expression language.
<form:form modelAttribute="owner">
<table>
<tr>
<th>
First Name: <form:errors path="firstName" cssClass="errors"/>
<br/>
<form:input path="firstName" size="30" maxlength="80"/>
</th>
</tr>
.
.
.
</form>
When using Spring, we need to specify the model attribute name as the value of the modelAttribute
attribute of the Spring <form:form>
tag and then use the corresponding property name as the value of the path
attribute of the various input fields generated by Spring’s form tag library.
MVC Models in Java EE
When using Java EE and JSF, all we need to do is annotate a class with the @ManagedBean
annotation, after which this class and its properties will be available from the view via the unified expression language. In the JSF version of the Pet Clinic application, JPA entities are used as the model. Instead of accessing them directly, each controller has a getSelected()
method that returns the corresponding JPA entity; for example, OwnerController.java has a getSelected()
method that returns an instance of Owner.
public Owner getSelected() {
if (current == null) {
current = new Owner();
selectedItemIndex = -1;
}
return current;
}
The Owner JPA entity can then be accessed from the view by invocation of the getSelected() method via the unified expression language.
<h:inputText id="firstName"value="#{ownerController.selected.firstName}"
title="#{bundle.CreateOwnerTitle_firstName}" />
View
The view in the MVC design pattern is responsible for displaying the user interface to the user.
Spring Views
The typical view technology used with Spring MVC is JSP with the JavaServer Pages Standard Tag Library (JSTL
) and custom Spring JSP tags. With JSF, Facelets is the preferred view technology. Although JSP is a mature technology, Facelets offers a superior development experience. For starters, Facelets pages are standard XHTML pages with custom namespaces; additionally, JSF provides components for commonly used functionality.
To illustrate, let’s take a look at the listing for generating a list of owners in the Spring version of the application.
<table>
<tr>
<thead>
<th>Name</th>
<th>Address</th>
<th>City</th>
<th>Telephone</th>
<th>Pets</th>
</thead>
</tr>
<c:forEach var="owner" items="${selections}">
<tr>
<td>
<a href="owner.do?ownerId=${owner.id}">
${owner.firstName} ${owner.lastName}</a>
</td>
<td>${owner.address}</td>
<td>${owner.city}</td>
<td>${owner.telephone}</td>
<td>
<c:forEach var="pet" items="${owner.pets}">
${pet.name}
</c:forEach>
</td>
</tr>
</c:forEach>
</table>
As you can see, with Spring we need to resort to an HTML table with nested JSTL tags to generate the list. Now let’s take a look at the equivalent page in the Java EE version
Java EE Views
Java EE uses JSF with Facelets as its default view technology. The following listing illustrates how to implement a table displaying dynamic data with this technology.
<h:dataTable value="#{ownerController.items}" var="item"
border="0" cellpadding="2" cellspacing="0"
rowClasses="jsfcrud_odd_row,jsfcrud_even_row"
rules="all" style="border:solid 1px">
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_firstName}"/>
</f:facet>
<h:outputText value="#{item.firstName}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_lastName}"/>
</f:facet>
<h:outputText value="#{item.lastName}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_address}"/>
</f:facet>
<h:outputText value="#{item.address}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_city}"/>
</f:facet>
<h:outputText value="#{item.city}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_telephone}"/>
</f:facet>
<h:outputText value="#{item.telephone}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value=""/>
</f:facet>
<h:commandLink
action="#{ownerController.prepareView}"
value="#{bundle.ListOwnerViewLink}"/>
<h:outputText value=" "/>
<h:commandLink
action="#{ownerController.prepareEdit}"
value="#{bundle.ListOwnerEditLink}"/>
<h:outputText value=" "/>
<h:commandLink
action="#{ownerController.destroy}"
value="#{bundle.ListOwnerDestroyLink}"/>
</h:column>
</h:dataTable>
In the Java EE version with Facelets, we can take advantage of the JSF dataTable component, which takes care of dynamically building tabular data for us. In general, developing Web applications with JSF and Facelets takes less mixing and matching of HTML tags with custom technology tags, and a lot of functionality—such as generating a table from dynamic data, as shown above—is already provided.
Controller
The controller in MVC is responsible for processing user input and navigating between views.
Spring Controllers
When using Spring (version 2.5 and later), controllers are annotated with the @Controller
annotation. For this to work, the following line needs to be added to the Spring MVC configuration file:
<context:component-scan base-package="org.springframework.samples.petclinic.web"/>
This line is necessary so that the Spring container knows that it needs to scan for Spring components in the package specified by the base-package
attribute. Although this approach is certainly easier than in previous versions of Spring, and controllers no longer have to be configured in the Spring XML configuration file, we still need to remember to add the above line to the configuration file so component scanning will work.
In Spring 2.5 and later, we typically annotate a couple of methods in the controllers with the @RequestMapping
annotation. One of the methods will handle HTTP GET requests, and the other one will handle HTTP POST requests.
<h:dataTable value="#{ownerController.items}" var="item"
border="0" cellpadding="2" cellspacing="0"
rowClasses="jsfcrud_odd_row,jsfcrud_even_row"
rules="all" style="border:solid 1px">
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_firstName}"/>
</f:facet>
<h:outputText value="#{item.firstName}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_lastName}"/>
</f:facet>
<h:outputText value="#{item.lastName}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_address}"/>
</f:facet>
<h:outputText value="#{item.address}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_city}"/>
</f:facet>
<h:outputText value="#{item.city}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText
value="#{bundle.ListOwnerTitle_telephone}"/>
</f:facet>
<h:outputText value="#{item.telephone}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value=""/>
</f:facet>
<h:commandLink
action="#{ownerController.prepareView}"
value="#{bundle.ListOwnerViewLink}"/>
<h:outputText value=" "/>
<h:commandLink
action="#{ownerController.prepareEdit}"
value="#{bundle.ListOwnerEditLink}"/>
<h:outputText value=" "/>
<h:commandLink
action="#{ownerController.destroy}"
value="#{bundle.ListOwnerDestroyLink}"/>
</h:column>
</h:dataTable>
The value of the method
attribute of the @RequestMapping
annotation specifies what type of HTTP request will be handled by the annotated method. Typically, the method that handles an HTTP GET request is responsible for displaying a form to be filled out by the user and the method handling HTTP POST requests is responsible for processing data entered by the user. Each of these methods must return a string that specifies how to dispatch the request after processing is done.
If we need to display a landing page, the way to resolve the appropriate JSP needs to be configured in the Spring XML configuration file as follows:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
If we need to send the request to a different controller, the receiving controller must have a method decorated with the @RequestMapping
annotation, with its value attribute set to the URL it is meant to handle.
In the Spring version of Pet Clinic, ClinicController.java has an ownerHandler() method annotated as follows:
@RequestMapping("/owner.do")
public ModelMap ownerHandler(@RequestParam("ownerId") int ownerId) {
return new ModelMap(this.clinic.loadOwner(ownerId));
}
Because the value of the @RequestMapping
annotation is “/owner.do,”
control is sent to the processSubmit()
method above when it finishes.
Java EE/JSF Controllers
For implementing MVC controllers with JSF, the controller class simply needs to be annotated with the JSF @ManagedBean
annotation. No annotation is necessary to mark the class as a controller, nor is there any need for XML configuration.
Methods that handle HTTP POST requests in JSF must take no arguments and return a string. For example, the following method in OwnerController.java
updates an existing owner:
public String update() {
try {
getFacade().edit(current);
JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").
getString("OwnerUpdated"));
return "View";
} catch (Exception e) {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").
getString("PersistenceErrorOccured"));
return null;
}
}
Note that it isn’t necessary to use any annotations on the method.
You can invoke controller methods from JSF command components, usually from a command button, by adding a unified expression language expression as the value of the component’s action
attribute.
<h:commandLink action="#{ownerController.update}"
value="#{bundle.EditOwnerSaveLink}"/>
Remember that by default, a JSF managed bean’s name is its class name with a lowercase first letter. The text after the dot is the name of the method to be invoked, and we know by convention that it will be public, take no arguments, and return a string
.
Going back to the method above, note that if no exception is caught, it will return the string
value “View”—by convention, JSF looks for a file called View.xhtml
and navigates to said page when the method finishes processing. Again, there is no need to configure this anywhere—it is just the way JSF works.
This is an illustration of how JSF relies a lot on convention and requires very little configuration.
Data Access and Transaction Management
For developing applications that need to access data from a database, it is customary to use the data access object (DAO) design pattern. With this pattern, data access objects (DAOs) encapsulate all interaction with the database.
Spring DAOs
With Spring, DAOs are usually annotated with the @Repository
annotation. This annotation marks the class as a DAO and translates the object-relational mapping tools (typically Hibernate) into a Spring-specific DataAccessException
.
In the Spring version of Pet Clinic, HibernateClinic.java
is a DAO and is therefore annotated with the @Repository
annotation.
package org.springframework.samples.petclinic.hibernate;
/* imports omitted */
@Repository
@Transactional
public class HibernateClinic implements Clinic {
@Autowired
private SessionFactory sessionFactory;
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<Vet> getVets() {
return sessionFactory.getCurrentSession().createQuery(
"from Vet vet order by vet.lastName, vet.firstName").list();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<PetType> getPetTypes() {
return sessionFactory.getCurrentSession().createQuery(
"from PetType type order by type.name").list();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<Owner> findOwners(String lastName) {
return sessionFactory.getCurrentSession().createQuery(
"from Owner owner where owner.lastName like :lastName")
.setString("lastName", lastName + "%").list();
}
@Transactional(readOnly = true)
public Owner loadOwner(int id) {
return (Owner) sessionFactory.getCurrentSession().load(
Owner.class, id);
}
@Transactional(readOnly = true)
public Pet loadPet(int id) {
return (Pet) sessionFactory.getCurrentSession().load(Pet.class, id);
}
public void storeOwner(Owner owner) {
// Note: Hibernate3's merge operation does not reassociate the object
// with the current Hibernate Session. Instead, it will always copy the
// state over to a registered representation of the entity. In case of a
// new entity, it will register a copy as well but will not update the
// ID of the passed-in object. To still update the IDs of the original
// objects too, we need to register Spring's
// IdTransferringMergeEventListener on our SessionFactory.
sessionFactory.getCurrentSession().merge(owner);
}
public void storePet(Pet pet) {
sessionFactory.getCurrentSession().merge(pet);
}
public void storeVisit(Visit visit) {
sessionFactory.getCurrentSession().merge(visit);
}
}
After the DAO has been annotated with @Repository
, it needs to be registered with the Spring container via the <bean> tag, as in Pet Clinic
<bean id="clinic" class="org.springframework.samples.petclinic.hibernate.HibernateClinic"/>
or the Spring container needs to be configured to scan components automatically, as explained earlier.
When dealing with databases, we usually need our methods to be transactional, so that we don’t risk ending up with inconsistent data in the database. In Spring you can accomplish this by annotating either the class or individual methods with the @Transactional
annotation.
Before we move on to Java EE, it is worth noting that it is a very common practice in the Spring world to use Spring-specific object-relational-mapping templates such as HibernateTemplate
to handle data access. Although these templates somewhat simplify data access code, they tie the application to the Spring API.
Java EE DAOs
When working with Java EE, it is typical to employ stateless session beans as DAOs. All we need to do to turn a Java class into a stateless session bean is to annotate it with the @Stateless
annotation. The DAOs in the Java EE version of Pet Clinic implement the Facade design pattern, meaning that they offer an interface that is simpler than JPA’s EntityManager
interface.
package com.ensode.petclinicjavaee.session;
//imports omitted
@Stateless
public class OwnerFacade extends AbstractFacade<Owner> {
@PersistenceContext(unitName = "PetClinicJavaEEPU")
private EntityManager em;
protected EntityManager getEntityManager() {
return em;
}
public OwnerFacade() {
super(Owner.class);
}
}
The above DAO (as well as all of the other DAOs in the Java EE version of the application) extends AbstractFacade
, which contains generic methods for handling all JPA entities in the application (see part 2 of this series for a listing of AbstractFacade
). It is worth noting that AbstractFacade
is not a session bean, which serves to illustrate that session beans can extend POJOs. There is no equivalent to the Spring @Transactional
annotation in Java EE, because all methods in an EJB are transactional by default.
NOTE: If for any reason, we need to implement a nontransactional method in an EJB, we can annotate it with the @TransactionAttribute annotation and set its value attribute to NOT_SUPPORTED. See chapter 43 of the
Java EE 6 Tutorial for details.
In summary, in most cases, all we need to do to implement a DAO is to add the @Stateless
annotation to the class, which enables us to take advantage of “free” transaction handling offered by the EJB container. In most cases, there is no need for any additional annotations or for XML configuration.
Dependency Injection
Dependency injection is a design pattern in which the dependencies of a class are automatically injected by a container. Both Spring and Java EE support dependency injection.
Spring Dependency Injection
With Spring, dependency injection is typically done via the @Autowired
annotation. You can see this in action in the Hibernate implementation of the Clinic DAO interface in the Spring version of the application.
package org.springframework.samples.petclinic.hibernate;
//imports omitted
@Repository
@Transactional
public class HibernateClinic implements Clinic {
@Autowired
private SessionFactory sessionFactory;
.
.
.
}
For this annotation to work, the following tag needs to be added to the Spring XML configuration file.
<context:annotation-config />
When the Spring container finds the @Autowired
annotation, it injects a bean of the appropriate type into the annotated property.
Java EE Dependency Injection
With JSF, managed beans can be injected into one another via the @ManagedProperty
annotation. For example, if we have two JSF managed beans named Foo
and Bar
and Bar
has a property of type Foo
, we can automatically inject the dependency as follows:
package com.ensode.foo;
//imports omitted
@ManagedBean
@RequestScoped
public class Bar {
@ManagedProperty("#{foo}")
private Foo foo;
/** Creates a new instance of Bar */
public Bar() {
}
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
}
The @ManagedProperty
default value for its value
attribute must be a unified expression language expression resolving to the bean we are injecting.
If a Java EE application uses Contexts and Dependency Injection (CDI), dependency injection is even simpler. The injected property needs to be annotated with the @Inject
annotation, and there is no need to specify a bean name.
Summary and Conclusion
In this series of articles, we developed a Java EE version of Spring’s Pet Clinic application. We saw how the advanced tooling provided by NetBeans enables us to quickly develop a Java EE application. After that we went through the generated code to make sure we understood it. We then made a few minor tweaks to the application and ended up with a very usable application that had taken very little coding on our part.
Once we were done building the Java EE version of the application, we compared it with the Spring version, noting that the original version has several dependencies whereas the Java EE version has none, because it takes advantage of all the services provided by the Java EE application server.
Finally, we compared how to implement similar functionality such as MVC and DAO implementation, transaction management, and dependency injection with Spring and Java EE. In every case with Spring, some XML configuration needs to be done besides adding annotations to the code. Java EE relies on convention, and in most cases, no XML configuration is needed in order to implement these services.
Although newer versions of Spring rely a lot less on explicit XML configuration than earlier versions, there are always a few little lines here and there that we need to add to an XML configuration file in order to get most of the Spring annotations to work, violating the DRY (don’t repeat yourself) principle. In the vast majority of cases with Java EE, no configuration is needed and annotations are sufficient to do the job.
Additionally, Spring applications tend to have several dependencies, because they are meant to run in a “lightweight” Servlet container such as Tomcat or Jetty and these containers don’t provide all the required functionality. In contrast, Java EE applications are meant to be deployed in a full-blown Java EE 6 application server such as Oracle GlassFish Server. Java EE 6 application servers provide most of the enterprise functionality we need, freeing us from having to manage several external dependencies.
For these reasons, I always recommend Java EE over Spring for enterprise application development.
See Also
- Spring to Java EE Migration, Part 1
- Spring to Java EE Migration, Part 2
- Spring to Java EE Migration, Part 3
- Java EE 6
- Java EE 6 Tutorial
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 masters degree in software engineering from Southern Methodist University.
Join the Conversation
Join the Java community conversation on Facebook, Twitter, and the Oracle Java Blog!