Stripes Spring JPA

Skip to end of metadata
Go to start of metadata

Stripes Spring JPA

Following best practices can help create applications faster, keep them easy to maintain while allowing for future unplanned evolutions. The purpose of the following minimal example application is an introduction to building scalable applications using Stripes, Spring and JPA with minimal configuration.

Stripes is a presentation framework which aims to make developing web applications in Java easy. Spring helps us to create and manage reusable business and data-access objects that are not tied to specific Java EE services. Spring objects can also be reused across Java EE environments (Web or EJB), standalone applications and test environments. JPA, a Java standard part of the EJB 3.0 specification, helps us implement a persistence layer that is vendor neutral, allowing to switch between persistence providers such as Hibernate, Toplink, iBatis or OpenJPA.

Both Spring and JPA encourage a layered architecture which helps address the complexity of Java enterprise application development, and with Stripes in the mix (and Java 5 Annotations), we are now able to build large scalable Java EE applications with minimal configuration for maximum productivity.

The example application has the following layout (excluding the META-INF folder) :

index.jsp
/WEB-INF
  |->  applicationContext.xml
  |->  web.xml
  |->  /lib
  |      |->  commons-logging.jar
  |      |->  cos.jar *
  |      |->  log4j.jar *
  |      |->  openjpa.jar *
  |      |->  persistence.jar
  |      |->  spring.jar
  |      |->  stripes.jar
  |->  /classes
  |      |->  StripesResources.properties
  |      |->  /action
  |      |      |->  ActionExample.class
  |      |      |->  BaseActionBean.class
  |      |->  /dao
  |      |      |->  /impl
  |      |      |      |->  Dao.class
  |      |      |      |->  DaoExample.class
  |      |      |->  IDao.class
  |      |      |->  IDaoExample.class
  |      |->  /model
  |      |      |->  /impl
  |      |      |      |->  ModelExample.class
  |      |      |->  IModelExample.class
  |      |->  /service
  |      |      |->  /impl
  |      |      |      |->  ServiceExample.class
  |      |      |->  IServiceExample.class

* In the lib directory the jars with an asterix could be replaced with other implementations. The others are required for the example application to function (except for spring.jar which contains more than we need). The purpose of cos.jar and log4j.jar is explained in the Stripes Quick Start Guide. openjpa.jar is just an implementation of JPA in one handy jar.

Stripes and Spring Configuration

We start off in the web.xml file by configuring the Spring ContextLoaderListener [1] and contextConfigLocation [2]. Next we configure the Stripes Filter to scan the action package for our action beans [3] and use the SpringInterceptor [4]. The rest of the configuration is classic web.xml stuff to do with Stripes intercepting *.action and *.jsp requests, before finishing off with a welcome file index.jsp [5].

"web.xml"
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  id="WebApp_ID" version="2.5">
  <display-name>StripesSpringJPA</display-name>

  <listener>         <!-- [1] -->
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <context-param>    <!-- [2] -->
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
  </context-param>

  <filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
    <init-param>     <!-- [3] -->
      <param-name>ActionResolver.Packages</param-name>
      <param-value>action</param-value>
    </init-param>
    <init-param>     <!-- [4] -->
      <param-name>Interceptor.Classes</param-name>
      <param-value>
            net.sourceforge.stripes.integration.spring.SpringInterceptor
      </param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>StripesFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

  <filter-mapping>
    <filter-name>StripesFilter</filter-name>
    <servlet-name>StripesDispatcher</servlet-name>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

  <servlet>
    <servlet-name>StripesDispatcher</servlet-name>
    <servlet-class>net.sourceforge.stripes.controller.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>StripesDispatcher</servlet-name>
    <url-pattern>*.action</url-pattern>
  </servlet-mapping>

  <welcome-file-list><!-- [5] -->
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
Configuration References
For more information consult the relevant configuration references for Stripes and Spring.

Spring Configuration

In the applicationContext.xml file specified by the contextConfigLocation context param, we simply instruct Spring to scan two of our packages for our reusable business [1] and data-access objects [2].

"applicationContext.xml"
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:component-scan base-package="service" /> <!-- [1] -->
    <context:component-scan base-package="dao" />     <!-- [2] -->
</beans>

The JSP

We use a minimal JSP to be able to submit a form to our ActionExample action bean. Upon submission of the form we will be forwarded back to this JSP and a list of messages will appear.

<%@taglib prefix="stripes"
          uri="http://stripes.sourceforge.net/stripes.tld" %>
<html><head><title>TestAction</title></head><body>
  <stripes:form beanclass="action.ActionExample">
	  Messages : ${actionBean.messages}<br />
	  <stripes:submit name="searchNumbers" value="Go !" />
  </stripes:form>
</body></html>

This guide not being a 'best practice' for how to use the Stripes tags, we specified the value of the <stripes:submit /> using the value attribute. We should have used a resource bundle, just like we should have specified a doctype etc, etc.

The Action Bean

Next we have the ActionExample class which extends our own BaseActionBean class. To create Stripes action beans we only need to implement the ActionBean interface. Although using our own base class allows us to hide any customizations to the ActionBeanContext that we may make, like implementing our own to facilitate state management and unit testing.

package action;

import java.util.List;

import model.IModelExample;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.HandlesEvent;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.integration.spring.SpringBean;
import service.IServiceExample;

public class ActionExample extends BaseActionBean {
	@SpringBean
	private IServiceExample serviceExample;
	private List<IModelExample> modelExamples;

	@DefaultHandler
	public Resolution welcome() {
		return new ForwardResolution("/index.jsp");
	}

	@HandlesEvent(value="searchNumbers")
	public Resolution searchNumbers() {
		modelExamples = serviceExample.searchMessages();
		return new ForwardResolution("/index.jsp");
	}

	public List<IModelExample> getMessages() {
		return modelExamples;
	}

	protected IServiceExample getServiceExample() {
		return serviceExample;
	}

	protected void setTestService(IServiceExample serviceExample) {
		this.serviceExample = serviceExample;
	}
}

The meat of the above example comes in the form of Stripes' @SpringBean annotation. Stripes uses the SpringInterceptor to inject Spring beans into ActionBeans after the ActionBean is instantiated. For more specific information consult Spring with Stripes.

The rest of the action bean code is all classic stuff, again, explained in the Stripes Quick Start Guide.

"action.BaseActionBean.java"
package action;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;

public class BaseActionBean implements ActionBean {
	private ActionBeanContext context;

	public ActionBeanContext getContext() {
		return context;
	}

	public void setContext(ActionBeanContext context) {
		this.context = context;
	}
}
Custom ActionBeanContext
To see more best practices in action have a look at the State Management guide which allows us to abstract away the HttpSession and facilitates unit testing our action beans.

The Service Layer

Next comes the service interface and class examples :

"service.IServiceExample.java"
package service;

import java.util.List;

import model.IModelExample;

public interface IServiceExample {
	List<IModelExample> searchMessages();
}
"service.impl.ServiceExample.java"
package service.impl;

import java.util.List;

import model.IModelExample;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import service.IServiceExample;

import dao.IDaoExample;

@Service
public class ServiceExample implements IServiceExample {
	@Autowired
	private IDaoExample daoExample;
	
	public List<IModelExample> searchMessages() {
		return daoExample.searchMessages();
	}

	public IDaoExample getDaoExample() {
		return daoExample;
	}

	public void setDaoExample(IDaoExample daoExample) {
		this.daoExample = daoExample;
	}
}

In the above service example we use two Spring stereotype annotations. The @Service annotation allows us to declare a Spring managed reusable business object, which we inject into our action bean with Stripes' @SpringBean annotation. The @Autowired annotation allows us to inject our Spring managed reusable data-access object, which we'll take a look at next.

Best Practices
The Spring @Service annotation also allows transparent use of declarative transaction management with some added Spring configuration.

The DAO Layer

Rather than re-coding the same basic DAO mechanisms over and over again, we use a generic (and generified) interface which helps to hide the details of the underlying persistence mechanism, which leaves us to only declare and code our specific business needs. This also makes it easier to test the service and DAO layers with dynamic mock implementations.

"dao.IDaoExample.java"
package dao;

import java.util.List;

import model.IModelExample;
import model.impl.ModelExample;

public interface IDaoExample {
	List<IModelExample> searchMessages();
}
"dao.impl.DaoExample.java"
package dao.impl;

import java.util.Arrays;
import java.util.List;

import model.IModelExample;
import model.impl.ModelExample;

import org.springframework.stereotype.Repository;

import dao.IDaoExample;

@Repository
public class DaoExample extends Dao<ModelExample, Long> implements IDaoExample {
	public List<IModelExample> searchMessages() {
		IModelExample modelSpring = new ModelExample();
		modelSpring.setMessage("Spring");
                
		IModelExample modelJPA = new ModelExample();
		modelJPA.setMessage("JPA");
                
		return Arrays.asList(modelSpring, modelJPA);
	}
}

Again, keeping the example simple we dont actually use our generified DAO base class. This means the application can demonstrate how everything works together, without getting bogged down with the configuration details of a DataSource or a JPA EntityManagerFactory. For example, the above searchMessages() method could have used the following code :

public List<IModelExample> searchMessages() {
	return findAll();
}

Or the service layer could have used return daoExample.findAll(). The findAll() method is detailed below along with the generified generic DAO base class and base interface.

More Best Practices
With the @Repository annotation Spring allows us to transparently translatate exceptions to save us from unnecessarily tying our application to the implementation strategy of proprietory vendor exceptions.
"dao.IDao.java"
package dao;

public interface IDao<T, PK> {
	void persist(T entity);

	void remove(T entity);

	void merge(T entity);

	T find(PK id);

	List<T> findAll();
}
"dao.impl.Dao.java"
package dao.impl;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import dao.IDao;

public abstract class Dao<T, PK> implements IDao<T, PK> {
	protected Class<T> entityType;

	//@PersistenceContext
	protected EntityManager entityManager;

	@SuppressWarnings("unchecked")
	public Dao() {
		ParameterizedType genericSuperclass = (ParameterizedType) getClass()
				.getGenericSuperclass();
		this.entityType = (Class<T>) genericSuperclass
				.getActualTypeArguments()[0];
	}

	public T find(PK id) {
		return entityManager.find(entityType, id);
	}

	public void persist(T entity) {
		entityManager.persist(entity);
	}

	public void remove(T entity) {
		entityManager.remove(entity);
	}

	public void merge(T entity) {
		entityManager.merge(entity);
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		String all = "select x from " + entityType.getSimpleName() + " x";
		Query query = entityManager.createQuery(all);
		return query.getResultList();
	}
}

The above generified Dao implementation is by no means comprehensive but gets us off to a good start. The @PersistenceContext annotation has been commented out for the purpose of this guide, which stops Spring from throwing an exeption when it can't find a configured EntityMangerFactory. This keeps our Spring configuration to a minimum.

The Model

The model example is just a wrapper around a simple message. If it were to be persisted we would need a table called ModelExample containing two columns, an integer (of sufficient size) and a varchar, named id and message respectively.

"model.IModelExample.java"
package model;

public interface IModelExample {
	String getMessage();

	void setMessage(String message);
}
"model.impl.ModelExample.java"
package model.impl;

import javax.persistence.Entity;
import javax.persistence.Id;

import model.IModelExample;

@Entity
@Table
public class ModelExample implements IModelExample {
	@Id
	private Long id;
	@Column
	private String message;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getMessage() {
		return message;
	}
	
	public void setMessage(String message) {
		this.message = message;
	}
	
	@Override public String toString() {
		return getMessage();
	}
}

The JPA @Entity annotation designates Java classes as JPA persistent entities. The Table, Id and @Column annotations describe the where and how to persist the data contained in the class.

JPA Reference
For more detailed information about JPA annotations consult the JPA Section of the Java EE 5 Tutorial.

You'll notice how there is no @GeneratedValue or @SequenceGenerator annotations beside the @Id annotation. Although we Stripers love annotations, specifying sequence generation strategies in an external orm.xml via the persistence.xml file will help the application to evolve. Since unplanned changes rear their ugly heads all too often, it's better to keep this sort of information separate from persistence information. That way, a change to the database or database sequence generation strategies would leave your model unchanged.

You'll also notice how we haven't talked about the persistence.xml or orm.xml file. This was done on purpose so as not to propose a certain way of configuring JPA as a 'best practice', as well as keeping the guide to a minimum in terms of configuration. Although if you want to see 3 ways to setup JPA in a Spring environment, have a look at this.

The above application will compile and run successfully using Spring managed beans injected into our Stripes ActionBeans. The next step would be to configure a JPA EntityManagerFactory, choose an implementation and choose/create a database to persist the model to.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.