REST CRUD: Jersey, Spring and JPA

Introduction

This is a follow-up to my previous article Jersey, Spring and JPA.  In this entry we will enhance the functionality of the REST service to include create,  read, update and delete (CRUD) functionality.  Additionally we have provided a HTML page to interact with the REST CRUD application.  The client uses AJAX through the Prototype Javascript library. 

Representational State Transfer (REST)

I won’t say too much on this since there are lots of well expressed articles on the web about this architecture.  We are, however, interested in discussing it with respect to how we consider setting up our REST implementation.

 The basic interface that we wish to satisfy is:

PersonService
+getPersons() get all persons from the database
+getPerson() get person of id from database
+deletePerson() delete person(s) of id(s) from the database
+savePerson() create new person record
+updatePerson() update person

 

We are going to identify the functionality of the solution through a combination of the PATH (/persons, /persons/{id}) and the HTTP action that we are preforming: GET, PUT, POST or DELETE.  The two tables show the resulting behaviour of the system give the combination of the PATH and action.

/persons
GET list all persons in database
PUT unused
POST add new person
DELETE unused

 

/persons/{id}
GET get person information
PUT update person information
POST unused
DELETE delete person information

 

We have implemented this design in two ways using the JAX-RS api.  The first way uses the path to pass the information to the REST service.  The mapped information is sent by a slash ‘/’ delmited list.   So for instance when POSTING a new person into the system the URL would look as follows:

POST http://localhost:8080/crud/webresources/persons/name/david/age/44

or a update would be

PUT http://localhost:8080/crud/webresources/person/1/name/david/age/44

Alternatively we may utilizes the services of both the @PathParam and @QueryParam annotations.  The may that uses both PathParam and QueryParam is my prefered approach.  I feel it leads to more general solutions. Its’ URLs would look as follows:

POST http://localhost:8080/crud/webresources/persons/?name=david&age=44

or a update would be

PUT http://localhost:8080/crud/webresources/person/id/1/name/david/age/44

 

The JAX-RS is not an RPC .  The annotations provide the facility of simplifying the code to interact with the HTTP session.  Through the use of the annotations we can produce clean, intuitive code productively.  There are many ways that one could apply these annotations we are going forward with the implementation as described in the tables below.

URIs when using the combination of path parameters and query parameters:

getPersons	GET	/
getPerson	GET	/{id}
deletePerson	DELETE	/{id}?id="1",id="2"....
savePerson	POST	/?name="name"&age="22"
updatePerson	PUT	/id="x"&name="name"&age="22"
URIs when using solely the path parameters:
getPersons	GET	/
getPerson	GET	/{id}
deletePerson	DELETE	/{ids}
savePerson	POST	/name/name/age/22
updatePerson	PUT	/id/x/name/name/age/22
 

For the rest of the article we will only be discussing the version that utilizes both PathParam and QueryParam. We will however be including the other version in the source code contained in the jar file link at the bottom of the page.

@Context UriInfo 

The javax.ws.rs.core.UriInfo is a useful class which, amoung other things, gives you access to the query parameters of the current request.  In our description above we descibe the DELETE command as:

deletePerson	DELETE	/{id}?id="1",id="2"....

the path uri path is:
/persons/{id}

We have designed the delete so it can take more than one id for deletion.  The combination of the path request with the HTTP action DELETE accounts for the first id but if we want to delete additional persons we then accept query parameters.  In the code we utilze the UriInfo to check to see if there are additional ids and if there are we iterate through them deleting them individually.

Note that the UriInfo object is constructed at the field level.  This component is also marked as request scope so that their is a new object constructed each time so that the uriInfo object will not cause threading issues.

 

@Component
@Path("/querypersons")
@Scope("request")
public class PersonResourceWs implements PersonResource {
	@Autowired
	PersonService personService;
	@Context
	UriInfo ui;

	@DELETE
	@Path("/{id}")
	@Produces("text/plain")
	public String deletePerson(@PathParam("id") String id) {
		if(id!=null) {
			deletePersonById(id);
		}

		MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
		List<String> ids = queryParams.get("id");
		if(ids == null) {
			log.info("\n\nThe ids is null");
		} else {
			for (String currentid : ids) {
				deletePersonById(currentid);
			}
		}
		return "success";
	}

 

 

Changes required in Prototype Javascript Library

The Prototype library piles DELETE, POST and PUT are all piped through POST.  This is down for some compatability issues with some browsers.  The problem is that it breaks the DELETE, POST and PUT HTTP request made through Prototype’s AJAX calls to our REST service.  I have removed this POST piping solution from the library by making the changes annotated below.  The altered prototype.js file is included in the distribution of the source code for this project with the described changes.

The client of the REST service is an HTML page which utilizes the Ajax services through

Ajax.Request = Class.create(Ajax.Base, {
  _complete: false,

  initialize: function($super, url, options) {
    $super(options);
    this.transport = Ajax.getTransport();
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.clone(this.options.parameters);
/* Commented out by David Sells to allow DELETE, PUT to pass through rather than be piped through POST
    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }
    */

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      // when GET, append parameters to URL
    	// Note David Sells added the delete, post and put to the condition below.
      if (this.method == 'get' || this.method == 'delete' || this.method == 'post' || this.method == 'put')
        this.url += (this.url.include('?') ? '&' : '?') + params;
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

 

The Client

The client (http://localhost:8080/crud/crud.html) looks like so:

Rest Client Image

Database Configuration and Sample Data

The database is configured through the crud.properties file:

jdbc.url=jdbc:mysql://localhost:3306/jpa
jdbc.username=user
jdbc.password=password
jdbc.driver=com.mysql.jdbc.Driver
hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
hibernate.hbm2ddl.auto=create

This configuration information is picked up in the applicationContext.xml. The PropertyPlaceholderConfigurer bean provides this service.

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource"
		p:driverClassName="${jdbc.driver}" p:url="${jdbc.url}" p:username="${jdbc.username}"
		p:password="${jdbc.password}" />
	<!-- Inject properties -->
	<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:crud.properties</value>
			</list>
		</property>
	</bean>

 Sample Data

We introduced a bean to load some data into the system.  Note that in our database configuration that we have set the property of the hbm2ddl.auto to create.  This means that on the initialization of the application, each time it runs, that the database tables are dropped and recreated.  The PersonDataloader.java class is in the link below.

package com.persistent.entity.util;

import java.util.ArrayList;
import java.util.List;

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

import com.persistent.entity.Person;
import com.persistent.service.PersonService;

public class PersonDataLoader {
	private List<Person> persons = new ArrayList<Person>();
	private PersonService personService;

	public void loadData() {
		for (Person person : persons) {
			personService.save(person);
		}
		persons.clear();
		persons = null;
	}

	public void setPersons(List<Person> persons) {
		this.persons = persons;
	}
	@Autowired
	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}

	public void init() {
		loadData();
	}
}

 

There is an interesting feature in Spring that we are utilizing to inject this data into the system.  Spring’s init-method allows you to add a callback method into the beans lifecycle.  The init-method call back will be called upon completing the construction of the bean.  So for our purposes when the bean has been loaded with data and the PersonService is injected we then save the data to the database.  The init() method performs this action and the snippet below shows how the init() method is configured with init-method.

	<!--  Some Test Data -->
	<bean id="personDataLoader" class="com.persistent.entity.util.PersonDataLoader" init-method="init">
		<property name="persons">
			<list>
				<bean class="com.persistent.entity.Person">
					<property name="name" value="Frank Zappa" />
					<property name="age" value="62" />
				</bean>
				<bean class="com.persistent.entity.Person">
					<property name="name" value="John Abercrombie" />
					<property name="age" value="63" />
				</bean>

 The Source Code

 Download the source code here: crud.zip

 

Articles of Interest

 

About The Author

David Sells is a computer consultant in Toronto, Ontario who specializing in Java Enterprise Development. He has  provided innovative solutions for clients including: IBM, Goldman Sachs, Merrill Lynch and Deutsche Bank.

He also holds the following certifications:

  • Sun Certified Enterprise Architect for the Java Platform, Enterprise Edition 5 (2009)
  • Sun Certified Web Component Developer
  • Sun Certified Java Programmer.

Contact: david@persistentdesigns.com

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>