Archive

Archive for September, 2012

Introduction to Spring Data JPA

September 15th, 2012 11 comments

The Spring Data JPA, a sub project of Spring Data is aimed at simplifying repository implementations. Repositories typically provide Create, Read, Update and Delete (CRUD) operations for domain objects. In this blog post, I will show how Spring Data JPA drastically reduces the effort needed to create repositories.

We will be using HSQLDB as the database and Hibernate as the JPA implementation provider. Here is a complete list project’s dependencies from Maven’s pom.xml file:


	<dependencies>	
		 <dependency>
			 <groupId>org.springframework.data</groupId>
	 		 <artifactId>spring-data-jpa</artifactId>
	 		 <version>1.1.0.RELEASE</version>
	 		 <exclusions>
	 		 	<exclusion>
					<groupId>org.slf4j</groupId>
					<artifactId>jcl-over-slf4j</artifactId>	 		 	
	 		 	</exclusion>
	 		 	</exclusions>
		</dependency>
		
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.1</version>
		</dependency>
		
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib-nodep</artifactId>
			<version>2.1_3</version>
		</dependency>
		
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.14</version>
		</dependency>
		
		<dependency>
			<groupId>aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.5.2a</version>
		</dependency>
		
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>3.1.2.RELEASE</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.1.0.Final</version>
		</dependency>
		
		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>2.2.8</version>
		</dependency>
		
	</dependencies>

Before we go about implementing repositories using Spring Data JPA, let’s create two domain objects Person and Address:


package com.inflinx.blog.springdata.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="PERSON")
public class Person {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="PERSON_ID")
	private Long id;

	@Column(name="FIRST_NAME")
	private String firstName;
	
	@Column(name="LAST_NAME")
	private String lastName;
	
	@Column(name="GENDER_CODE")
	private String genderCode;
		
	// Getters and Setters	
}

package com.inflinx.blog.springdata.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="ADDRESS")
public class Address {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="ADDRESS_ID")
	private int id;

	@Column(name="STREET")
	private String street;
	
	@Column(name="CITY")
	private String city;
	
	@Column(name="STATE")
	private String state;

	@Column(name="ZIPCODE")
	private int zipCode;
	
	@ManyToOne
	@JoinColumn(name="PERSON_ID")
	private Person person;
	
	// Getters and Setters
}	
	

Notice that the two domain classes have JPA annotations such as @Table and @Column that map these classes to relational tables. Also notice the Many To One relationship between Address and Person classes.

Simple JPA Repository

We now start by creating a Spring Data JPA repository for the Person class. This involves creating a repository interface as shown below. The PersonRepository interface extends the Spring Data JPA’s Repository marker interface and has one finder method. The findOne method follows Spring Data JPA’s naming convention for retrieving an entity using its ID.

package com.inflinx.blog.springdata.repository;

import org.springframework.data.repository.Repository;
import com.inflinx.blog.springdata.domain.Person;

public interface PersonRepository extends Repository < Person, Long > {
	public Person findOne(Long id);
}

Traditionally, we would also create an implementation class for this repository. However Spring Data JPA alleviates this need and provides an implementation class (proxy) automatically at runtime. For this to happen, we add the jpa:repositories bean declaration to our context file. Here is the complete context file:

<?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:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:repository="http://www.springframework.org/schema/data/repository"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.1.xsd
		http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
	
	<jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:schema.sql"/>
        <jdbc:script location="classpath:test-data.sql"/>
    </jdbc:embedded-database>
    
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
          p:data-source-ref="dataSource" p:persistence-xml-location="classpath:test-persistence.xml" p:jpaDialect-ref="jpaDialect">
    </bean>
    
    <tx:annotation-driven transaction-manager="transactionManager"/>  
       
	<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
	
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" 
          p:entity-manager-factory-ref="entityManagerFactory">
    </bean>
    
    <jpa:repositories base-package="com.inflinx.blog.springdata.repository" />
</beans>

The jdbc:embedded-database tag in the context file starts an in-memory database using HSQLDB and creates a datasource for it. The schema.sql file shown below has the DDL commands for creating the Person and Address table:

CREATE TABLE PERSON (PERSON_ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 
					FIRST_NAME VARCHAR(256), 
					LAST_NAME VARCHAR(256), 
					GENDER_CODE VARCHAR(1));

CREATE TABLE ADDRESS(ADDRESS_ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 
 					STREET VARCHAR(256), 
 					CITY VARCHAR(100), 
 					STATE VARCHAR(10), 
 					ZIPCODE INT,
 					PERSON_ID INT,
 					FOREIGN KEY (PERSON_ID) REFERENCES PERSON(PERSON_ID)); 

The test-data.sql file has the test data we will be using to test our repositories:

	INSERT INTO PERSON (FIRST_NAME, LAST_NAME, GENDER_CODE) VALUES ('John', 'Doe', 'M');
	INSERT INTO PERSON (FIRST_NAME, LAST_NAME, GENDER_CODE) VALUES ('Jane', 'Doe', 'F');
	INSERT INTO PERSON (FIRST_NAME, LAST_NAME, GENDER_CODE) VALUES ('Joe', 'Bloggs', 'M');

The entitiyManagerFactory bean uses test-persistence.xml file that has the standard JPA configuration.


<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">

  <persistence-unit name="springdata" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    
    <class>com.inflinx.blog.springdata.domain.Person</class>
    <class>com.inflinx.blog.springdata.domain.Address</class>
    
    <properties>
      <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
	  <property name="hibernate.show_sql" value="true"/>
	  <property name="hibernate.format_sql" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

This completes all the setup needed for creating a Spring Data JPA repository. Now let’s create a JUnit test class to test our implementation.


package com.inflinx.blog.springdata.repository;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;

import com.inflinx.blog.springdata.domain.Person;

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
@Transactional
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class PersonRepositoryTest {

	@Autowired
	private PersonRepository personRepository;
	
	@Test
	public void testPerson() {
		Person person = personRepository.findOne(1L);
		Assert.assertNotNull(person);
		System.out.println("First Name: " + person.getFirstName());
	}
	
}

Notice that we are simply injecting the PersonRepository implementation into the test. When you run the class, you should see that test has passed and “First Name: Jane” in your console.

CRUD JPA Repository

The above PersonRepository has only one finder method and might not be ideal for most real world cases. To address this, Spring Data JPA provides an aptly named CrudRepository interface that extends Repository interface and adds persistence, finder and delete methods. The table below lists some of the available CrudRepository methods:

save(S entity) Saves a given entity
save(Iterable entities) Saves all the given entities
findOne(ID id) Retrieves an entity by its id
exists(ID id) Checks if a given entity exists
findAll Returns all the entities
count Returns the number of all the entities
delete(ID id) Deletes an entity with the given id
delete(T entity) Delets a given entity

A new person repository interface that uses CrudRepository is given below.


package com.inflinx.blog.springdata.repository;

import org.springframework.data.repository.CrudRepository;

import com.inflinx.blog.springdata.domain.Person;

public interface PersonCrudRepository extends CrudRepository < Person, Long > {

}

Here is a JUnit test that tests the above repository:


package com.inflinx.blog.springdata.repository;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;

import com.inflinx.blog.springdata.domain.Person;

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
@Transactional
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class PersonCrudRepositoryTest {

	@Autowired
	private PersonCrudRepository personRepository;
	
	@Test
	@Rollback(false)
	public void testCreate() {
		Person p = new Person();
		p.setFirstName("Test");
		p.setLastName("Data");
		p.setGenderCode("M");
		
		p = personRepository.save(p);
		Assert.assertNotNull(p.getId());
		System.out.println("Created Id: " + p.getId());
	}	
}

Custom Finder Methods

Spring Data JPA makes it very easy to add new finder methods to repositories. For example, let’s say we want to retrieve all persons with a given last name. Here is the modified PersonCrudRepository repository with the new finder method:

package com.inflinx.blog.springdata.repository;

import java.util.List;
import org.springframework.data.repository.CrudRepository;
import com.inflinx.blog.springdata.domain.Person;

public interface PersonCrudRepository extends CrudRepository < Person, Long > {

	public List< Person > findByLastName(String lastName);
}

The convention for these finder methods is simply findByMemberName. It is also possible to combine multiple members into one finder method. So, findByLastNameAndFirstName(String lastName, String firstName) will find all persons with the given first name and last name.

Here is the JUnit test class to test the findByLastName method:

 

	@Test
	public void testFindByLastName() {
		
		List< Person > lastNameList = personRepository.findByLastName("Doe");
		Assert.assertEquals(2, lastNameList.size());
	}

Named Queries

The out of the box CRUD methods are sufficient for most cases. However, there are times where we might want to execute custom SQL Queries to retrieve data. For example, let’s say we want to count all the persons that have a given last name. To address such situations, Spring Data JPA provides a @Query annotation. Here is the new person repository with the @Query annotated method:

package com.inflinx.blog.springdata.repository;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

import com.inflinx.blog.springdata.domain.Person;

public interface PersonCrudRepository extends CrudRepository < Person, Long > {

	@Query("select count(p) from Person p where p.lastName = ?1")
	public Long getLastNameCount(String lastName);
	
}

Here is a test method for the above Query method:

	@Test
	public void testGetLastNameCount() {
		
		Long count = personRepository.getLastNameCount("Doe");
		Assert.assertEquals(2L, count.longValue());
	}

Custom Repository Implementation

For cases, where the out of the box repository implementations are not enough, Spring Data JPA makes it easy to provide custom implementations that would still integrate with the generic repository abstraction. To see this in action, let’s implement a custom Address repository. We start with a custom interface that declares the additional functionality.

package com.inflinx.blog.springdata.repository;

public interface AddressRepositoryCustom {
	
	public Long someRandomMethod();
	
}

The next step is to create a class that implements the custom functionality.

package com.inflinx.blog.springdata.repository.jpa;

import com.inflinx.blog.springdata.repository.AddressRepositoryCustom;

public class AddressRepositoryImpl implements AddressRepositoryCustom {

	public Long someRandomMethod() {
		return 143L;
	}
}

Finally, we create the AddressRepository interface that extends the custom interface as shown below:

public interface AddressRepository extends CrudRepository < Address, Long >, AddressRepositoryCustom {
	
}

Here is a test class that tests the custom method implementation:

package com.inflinx.blog.springdata.repository;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
@Transactional
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class AddressRepositoryCustomTest {
	
	@Autowired
	private AddressRepository personRepository;
	
	@Test
	public void testSomeMethod() {
		
		Assert.assertEquals(143L, personRepository.someRandomMethod().longValue());
	}
	
}

Spring Data JPA requires that you follow strict naming conventions when creating custom repository implementation (unless you provide additional configuration). For example, if the repository interface is AddressRepository, then the name of the custom interface should be AddressRepositoryCustom. If you don’t follow this convention, you will end up with errors similar to this:

Caused by: java.lang.IllegalArgumentException: Could not create query metamodel for method public abstract java.lang.Long com.inflinx.blog.springdata.repository.AddressRepositoryCustom.someRandomMethod()!
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:92)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:162)

Spring Async And Future – Report Generation Example

September 9th, 2012 9 comments

Spring 3.0 introduced the @Async annotation for executing tasks asynchronously. The asynchronous capabilities are highly useful in situations where we need to execute a long running task before allowing user input. For example, every time we open a Gmail message with attachment, Gmail performs a anti-virus scan before showing the download link to the user.

In this blog post, I will kick off a long running report asynchronously. Then I will use JQuery to periodically check if the report has been created. When the report is finally available, a link to the report is shown to the user.

We start by creating a ReportService as shown below. The generateReport method returns java.util.concurrent.Future interface. This interface allows the caller to retrieve the execution result at later time.

package com.inflinx.blog.springfuture.service;

import java.util.concurrent.Future;

import com.inflinx.blog.springfuture.domain.Report;

public interface ReportService {
	
	public Future generateReport();
	
}

The report service implementation is shown below. We simulate the “long running process” by calling the sleep method on the current thread. The method returns AsyncResult instance with the newly created Report as pass through value.


package com.inflinx.blog.springfuture.service;

import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import com.inflinx.blog.springfuture.domain.Report;

@Service("reportsService")
public class ReportServiceImpl implements ReportService {

	@Async
	public Future generateReport() {
		
		try {
			Thread.sleep(15000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		Report report = new Report();
		report.setName("New Report");
		report.setDescription("New Report Description");
		
		return new AsyncResult(report);
	}
	
}

We complete the service layer implementation by adding the following Spring configuration file:


<?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:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
       http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <context:annotation-config />
    
    <context:component-scan base-package="com.inflinx.blog.springfuture.service" />
       
    <task:annotation-driven />
       
</beans>       		

Now that we have completed our service layer, let’s create an MVC Controller that uses the ReportService to create a new report. The new report is then saved in the user’s session.

package com.inflinx.blog.springfuture.web.controller;

import java.util.concurrent.Future;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.inflinx.blog.springfuture.domain.Report;
import com.inflinx.blog.springfuture.service.ReportService;

@Controller
public class HomeController 
{
	@Autowired
	@Qualifier("reportsService")
	private ReportService reportService;
	
	@RequestMapping(value="/home.html", method=RequestMethod.GET)
	public String showHome(HttpSession session) 
	{
		Future report = reportService.generateReport();
		
		session.setAttribute("report", report);
		
		return "home";
	}	
}

To this controller, we then add a method that returns the status of the report generation. The method implementation simply retrieves the Future object and invokes the isDone method to check the status of the running task.

	@RequestMapping("/reportstatus.json")
	@ResponseBody
	public String reportStatus(HttpSession session) {
		Future report = (Future)session.getAttribute("report");
		
		if(report.isDone()) {
			System.out.println("Report Generation Done");
			return "COMPLETE";
		}
		else {
			System.out.println("Still Working on Report");
			return "WORKING";
		}
	}

We then add a method to the controller that shows the generated report as shown below:


@RequestMapping(value="/report.html", method=RequestMethod.GET)
	public String showReport(HttpSession session, Model model) throws InterruptedException, ExecutionException 
	{
		Future report = (Future)session.getAttribute("report");
		
		Report r = report.get();
		model.addAttribute("report", r);
		
		return "report";
	}

The report.jsp view returned by the above method is shown below:


<html>
<head>
	<title>Home</title>
</head>
<body>
<h1>
	Generated Report
</h1>

 Name: ${report.name} <br />
 Description: ${report.description}

</body>
</html>

We finally tie everything in the home.jsp view that gets returned by the showHome method. In the home.jsp, we use JQuery’s setInterval method to call the reportstatus.json URL every two seconds. Once we get the “COMPLETE” response, we show the download link.


<html>
<head>
	<title>Home</title>

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>

<script type="text/javascript">
	
	$(document).ready(function() {
		
		// Check The Status Every 2 Seconds
		var timer = setInterval(function() {
			
			$.ajax({
				  url: 'reportstatus.json',
				  success: function(data) {
					
					if(data === 'COMPLETE') {
						$('#reportLink').html("<a target='_target' href='report.html'>Download Report</a>");	
						clearInterval(timer);
					}
				  }
			});
			
		}, 2000);
	});
		
</script>

</head>
<body>
<h1>
  Report Generator
</h1>

<div id="reportLink">Please Wait While Your Report Is Being Generated</div>

</body>
</html>

Here is a screenshot of the application’s home.jsp page:

Here is a screenshot after the report has been generated:

The screenshot of the newly generated report:

Categories: Spring Tags: , ,