HowTo create a web application with Spring and JPA

One of my colleagues asked me recently to help him with building and configuring a simple web application with support of Spring IoC and JPA. Application should serve as a simple platform for testing and its only goal is persisting and reading of several entities. He wanted to learn how to add new services, how to write JUnit tests etc.

So instead of usual RTFM I decided to create a tutorial describing how to create such application and how to interconnect particular layers. There are for sure many ways how to achieve this goal and here is one on them. Source of the application is available here. This article does not contain all source files in order to keep the size on a reasonable level. So I describe here only parts which I consider interesting.

Application structure and Maven

Maven is a great tool for managing java-based projects. Having a common directory layout allows us to easily build WAR or EAR archives, generate documentation, run tests and others without complicated configuration. Very easy is also a 3rd party dependencies management. For example adding a dependency on junit is just a matter of following code:

<dependency>
    <groupid>junit</groupid>
    <artifactid>junit</artifactid>
    <version>4.8.1</version>
    <scope>test</scope>
</dependency>

And that’s all we need to define. And if junit has any dependency (so called transitive dependencies), then if would be detected by Maven and these are downloaded into a local repository as well. The complete description of Maven is out of the article’s scope.

Maven arechetype

Now we use the maven-archetype-webapp arechytepe in order to create a basic layout of the HelloWorld jsp application. Install Maven if you haven’t done it before and use the following command

mvn archetype:create -DgroupId=net.bytesource.prototype -DartifactId=spring-helloworld -DarchetypeArtifactId=maven-archetype-webapp

Maven created a new directory spring-helloworld containing a base directory layout of our application. To compile and build it call:

mvn package

A newly created subdirectory target contains the final package spring-helloworld.war. Maven can also help us to deploy the application into the web container Tomcat. Let’s add the maven-cargo plugin’s configuration into the Maven configuration file pom.xml.

<plugin>
    <groupid>org.codehaus.cargo</groupid>
    <artifactid>cargo-maven2-plugin</artifactid>
    <version>1.0.3</version>
    <configuration>
        <wait>true</wait>
        <container>
            <containerid>tomcat6x</containerid>
            <home>${project.build.directory}/${download.archive-dir.tomcat6x}/${download.archive-dir.tomcat6x}</home>
            <zipurlinstaller>
                <url>${download.url.tomcat6x}</url>
                <installdir>${project.build.directory}</installdir>
            </zipurlinstaller>
        </container>
    </configuration>
</plugin>

and two properties definition

<properties>
    <download.url.tomcat6x>http://ftp.sh.cvut.cz/MIRRORS/apache//tomcat/tomcat-6/v6.0.29/bin/apache-tomcat-6.0.29.zip</download.url.tomcat6x>
    <download.archive-dir.tomcat6x>apache-tomcat-6.0.29</download.archive-dir.tomcat6x>
</properties>

then use the plugin to download, install and start Tomcat container with our application.

mvn cargo:start

server should be running, you can check it at http://localhost:8080/spring-helloworld.

Note

Tomcat is installed into to a Maven working directory target so nothing is being installed into any system directory and therefore we don’t need any administrator access rights.

Import into Eclipse

I usually prefer the Eclipse development environment so I’m going to use the maven-eclipse-plugin in order to create an eclipse project out of our Maven one. As far as I know there is also a similar plugin for IntelliJIDEA. NetBeans is able to use Maven projects natively. So for Eclipse execute this command:

mvn eclipse:eclipse

and import as an external project.

Entity Employee and management beans

Let’s create an entity describing an employee, entity management bean responsible for an entity persistence and a simple class containing methods managing access to list of employees, called usually directly from the UI layer.
Entity Employe contains all employee’s attributes and a primary key ID. For an ID is ideal usage of the UUID, but the problem is that the only easy to use UUID generator is the one defined by the @GenericGenerator annotation from Hibernate. And that’s not the cleanest solution as we should use the pure JPA interface and not to stick to a implementation specific feature. But for now let’s keep the code simple and define the entity.

@Entity
@Table(name = "EMPLOYEE")
public class Employee implements Serializable {
    @Id
    @Column(name = "EMPLOYEE_ID", nullable = false, length = 32)
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy="uuid")
    private String id;
 
    @Column(name = "FIRST_NAME", length = 30)
    private String firstName;
 
    @Column(name = "LAST_NAME", length = 30)
    private String lastName;
 
    @Version
    @Column(name = "VERSION")
    private Long version;

The class is annotated by @Entity defining that the class is a JPA entity. Annotation @Table defines the DB table used for the table persistence.

To keep the example easier we skip the DAO layer, which is usually used as an intermediate between a business logic and a database layer. Using the JPA is anyway reducing the need to define a DAO layer even for a real production application. In our case will all entity realed operations be managed by the class EmployeeManagementServiceJpaImpl. I recommend to have a look on it right now. I’m just going to explain used annotations:

  • @Repository is used to mark classes working with an entity manager and Spring expects translation of DataAccessException. It also marks the class to be available for the autowiring strategy.
  • @PersistenceContext variable will contain an instance of the EntityManager
  • @Transacrtional declares that the method will be executed within a transaction. Without additional parameters will be connected to already running transaction or creates a new one in case when there isn’t any transaction running.

Next class is EmployeeUIServiceImpl.

@Service
public class EmployeeUIServiceImpl implements EmployeeUIService {
 
    @Autowired
    private EmployeeManagementService employeeManagementService;

a class from UI layer. Or said in better words, this class might be part of the UI layer if the architecture is created this way. I decided to define it to have the application designed consistently by independent layers and to be able to present a dependency injection feature. In this case is the attribute employeeManagementService injected by Spring as declared by annotation @Autowired. In general is an autowire strategy not a best practice, but we can use it to understand how Spring dependency injection works in a real application.

Spring and its configuration

The java code of our application is ready so let’s do some configuration.First we need to add dependencies on several Spring components, HyperSQL database, Hibernate and several other libraries which our application is using. For the complete definition have a look into the pom.xml file at the <dependencies> element content.

The Spring context configuration is located in the file src/main/resource/spring/helloworldContext.xml. Maven is able to put the file into the proper directory of the final package. Let’s go through interesting parts of the configuration:

<context:component-scan base-package="net.bytesource.prototype" />

This element defines a java package where all classes annotated by @Service or @Repository are collected into a list. And every bean from this list can be injected using an annotation @Autowired or @Resource. For example let’s have a look on the EmployeeUIServiceImpl class and it’s attribute employeeManagementService.

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
    </property>
    <property name="dataSource" ref="dataSource" />
</bean>

Configuration of the EntityManagerFactory. Parameter jpaVendorAdapter is the single configuration point for the JPA implementation. So changing of the persistence layer implementation to eg. Oracle TopLink is as easy as configuring this one property.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
    <property name="url" value="jdbc:hsqldb:mem:." />
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>

The datasource definition. We are going to use the HyperSQL – a database engine written completely in java.Our configuration will use the memory mode which does not need any additional settings. The engine creates a database when first client connects to it and all data is lost after the database is closed. HyperSQL could be of course also configured to use the file based database.

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>

Spring transaction manager. This one is not usually called directly from an application code. We are going to make an exception in our junit tests mentioned later on.

<tx:annotation-driven />

Every method annotated by @Transactional will be executed within a transaction.

<bean id="net.bytesource.prototype.service.EmployeeUIService" class="net.bytesource.prototype.service.impl.EmployeeUIServiceImpl" />

Spring bean definition. Spring creates an instance of the EmployeeUIServiceImpl class and assigns an ID to it. This bean is used for the ControllerServlet servlet implementation. We are using the autowire strategy so there is no need to declare manually all beans in the context. Spring does it for us if we annotate these by @Service, @Repository etc.

JUnit Tests

The main part of the application is ready so let’s verify that everything works as expected by creating unit tests. Maven could help us here again. All test classes located in the directory src/test/main are automatically compiled end executed before every build – unless this step is explicitly skipped. This approach guaranties that the build can succeed only if all test passed successfully.

Our test are going to write into database and afterwards read the content. Written records could influence results of other tests or other application components in a real application. One possible solution is to open a transaction right before the test starts and call a rollback after a test is finished. So we create the abstract class BaseSpringTestCase used as a parent class of all our tests and annotate it by @ContextConfiguration containing a path to the Spring context configuration. This parent class will contain methods for beginning and closing transactions.

The test class is extending BaseSpringTestCase. All test methods are public and annotated by @Test. Test body should be clear from the source so will just explain the transaction handling part:

@Test
public void persistAndFindTest(){
    beginRollbackOnlyTransaction();
    try {
        // test method .....
    }
    finally {
        commitTransaction();
    }
}

The transaction is created at the beginning of the method and is marked as rollback-only. This means that if the method runs without exception and the transaction is finished successfully (commit) and also if an exception was thrown, the transaction will always be finished by the rollback operation. It means that database will be restored to the status before transaction.

Tests can be executed by:

mvn testing

The test logging output including SQL statements generated by Hibernate are written into console. And test results can be found in the target/surefire-reports.

Web application

The next step is creating a simple UI for our application. We create a servlet and one jsp page. The page displays list of all employees and a form to insert a new employee into a database. The employee list is in the ControllerServlet.java retrieved from employeeUIService.getAllEmployeeNames() and passed into EmployeeViewer.jsp via a request attribute. Jsp page renders the list in a loop into the html table. The new employee record from the form is passed into the servlet where the persistence method employeeUIService.addEmployee(firstName, lastName) is called.

public void init() throws ServletException {
    WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
    employeeUIService = (EmployeeUIService) ctx.getBean(EmployeeUIService.class.getName());
}

The Spring managed bean employeeUIService is in the servlet retrieved manually. In the method init() we first get the WebApplicationContext and then a bean out of it. Notice that the bean ID is in fact a class name. This approach can help us to manage quite a lot of bean definitions in configuration files.

An important file for the web application is web.xml. So have a look on some important parts

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/classes/spring/helloworldContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

The parameter contextConfigLocation defines a path to the Spring context configuration. It’s the same file used also for JUnit tests. ContextLoaderListener is a listener initializing the Spring and its WebApplicationContext.

The last file is persistence.xml, in this case as very minimalistic. It defines a persistence unit including all entities (@Entity) – in our case only Employee.

These are all parts of our application. To compile, test, build a WAR and deploy into Tomcat use the following command

mvn package cargo:start

If everything went well, then your application should be available at http://localhost:8080/spring-helloworld/employees

Application’s source code

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*
*

elf + 13 =