Enterprise Java

Spring Boot and Spring Data REST – exposing repositories over REST

logo-spring-io

Exposing Spring Data repositories over REST is pretty easy with Spring Boot and Spring Data REST. With minimal code one can create REST representations of JPA entities that follow the HATEOAS principle. I decided to re-use Spring PetClinic’s JPA entities (business layer) as the foundation for this article.
 
 
 

Application foundation

The PetClinic’s model is relatively simple, but it consist of some unidirectional and bi-directional associations, as well as basic inheritance:

sbdr-petclinic-diagram

In addition, the Spring’s PetClinic provides SQL scripts for HSQLDB which makes that generating schema and populating it with sample data in my new application was super easy.

Project dependencies

As a base for the configuration I used Spring Initializr and I generated a basic Gradle project. In order to utilize Spring Data REST in a Spring Boot Application I added the following Boot Starters:

compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-data-rest")

In addition, I added HSQLDB dependency to the project:

compile("org.hsqldb:hsqldb:2.3.2")

The original project uses org.joda.time.DateTime for date fields and uses org.jadira.usertype.dateandtime.joda.PersistentDateTime that allows persisting it with Hibernate. In order to be able to use it in the new project I needed to add the following dependencies:

compile("joda-time:joda-time:2.4")
compile("org.jadira.usertype:usertype.jodatime:2.0.1")

While working with the API, I noticed that although the date fields in the original project were annotated with Spring’s @DateTimeFormat they were not properly serialized. I found out that I need to use @JsonFormatter, so another dependency was added to the build.gradle:

compile("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.4.2");

Once in the classpath, Spring Boot auto configures com.fasterxml.jackson.datatype.joda.JodaModule via org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.

Please note, that if you wanted to serialize Java 8 Date & Time types properly, you would need to add Jackson Datatype JSR310 dependency to project.

Initializing the database

To initialize data source I added schema-hsqldb.sql and data-hsqldb.sql files to src/main/resources. Finally, I several properties were added to application.properties:

spring.datasource.platform = hsqldb
   spring.jpa.generate-ddl = false
   spring.jpa.hibernate.ddl-auto = none

Now, at the application start up, files will be picked up automatically and the data source will be initialized and discovering the API will be much easier, as there is data!

Repositories

The general idea of Spring Data REST is that builds on top of Spring Data repositories and automatically exports those as REST resources. I created several repositories, one for each entity (OwnerRepository, PetRepository and so on). All repositories are Java interfaces extending from PagingAndSortingRepository.

No additional code is needed at this stage: no @Controllers, no configuration (unless customization is needed). Spring Boot will automagically configure everything for us.

Running the application

With the whole configuration in place the project can be executed (you will find link to the complete project in the bottom of the article). If you are lucky, the application will start and you can navigate to http://localhost:8080 that points to a collection of links to all available resources (root resource). The response’s content type is .

HAL

The resources are implemented in a Hypermedia-style and by default Spring Data REST uses HAL with content type application/hal+json to render responses. HAL is a simple format that gives an easy way to link resources. Example:

$ curl localhost:8080/owners/1
{
  "firstName" : "George",
  "lastName" : "Franklin",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/owners/1"
    },
    "pets" : {
      "href" : "http://localhost:8080/owners/1/pets"
    }
  }
}

In terms of Spring Data REST, there are several types of resources: collection, item, search, query method and association and all utilize application/hal+json content type in responses.

Collection and item resource

Collection resource support both GET and POST methods. Item resources generally support GET, PUT, PATCH and DELETE methods. Note that, PATCH applies values sent with the request body whereas PUT replaces the resource.

Search and find method resource

The search resource returns links for all query methods exposed by a repository whereas the query method resource executes the query exposed through an individual query method on the repository interface. Both are read-only therefore support only GET method.

To visualize that, I added a find method to OwnerRepository:

List<Owner> findBylastName(@Param("lastName") String lastName);

Which was then exposed under http://localhost:8080/owners/search:

$ curl http://localhost:8080/owners/search                                     
{                                                                              
  "_links" : {                                                                 
    "findBylastName" : {                                                       
      "href" : "http://localhost:8080/owners/search/findBylastName{?lastName}",
      "templated" : true                                                       
    }                                                                          
  }                                                                            
}

Association resource

Spring Data REST exposes sub-resources automatically. The association resource supports GET, POST and PUT methods.

and allow managing them. While working with association you need to be aware of text/uri-list content type. Requests with this content type contain one or more URIs (each URI shall appear on one and only one line) of resource to add to the association.

In the first example, we will look at unidirectional relation in Vet class:

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"),
        inverseJoinColumns = @JoinColumn(name = "specialty_id"))
private Set<Specialty> specialties;

In order to add existing specialties to the collection of vet’s specialties PUT request must be executed:

curl -i -X PUT -H "Content-Type:text/uri-list" -d $'http://localhost:8080/specialties/1\nhttp://localhost:8080/specialties/2' http://localhost:8080/vets/1/specialties

Removing the association can be done with DELETE method as below:

curl -i -X DELETE http://localhost:8080/vets/1/specialties/2

Let’s look at another example:

// Owner
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Pet> pets;

// Pet
@ManyToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name = "owner_id")
private Owner owner;

Setting owner of a pet can be done with the below request:

curl -i -X PUT -H "Content-Type:text/uri-list" -d "http://localhost:8080/owners/1" http://localhost:8080/pets/2/owner

But what about removing the owner? Since the owner must be always set for the pet, we get HTTP/1.1 409 Conflict whilst trying to unset it with the below command:

curl -i -X DELETE http://localhost:8080/pets/2/owner

Integration Tests

With Spring Boot, it is possible to start a web application in a test and verify it with Spring Boot’s @IntegrationTest. Instead of using mocked server side web application context (MockMvc) we will use RestTemplate and its Spring Boot’s implementation to verify actual REST calls.

As we already know, the resources are of content type application/hal+json. So actually, it will not be possible to deserialize them directly to entity object (e.g. Owner). Instead, it must be deserialized to org.springframework.hateoas.Resource that wraps an entity and adds links to it. And since Resource is a generic type ParameterizedTypeReference must be used with RestTemplate.

The below example visualizes that:

private RestTemplate restTemplate = new TestRestTemplate();

@Test
public void getsOwner() {
    String ownerUrl = "http://localhost:9000/owners/1";

    ParameterizedTypeReference<Resource<Owner>> responseType = new ParameterizedTypeReference<Resource<Owner>>() {};

    ResponseEntity<Resource<Owner>> responseEntity =
            restTemplate.exchange(ownerUrl, GET, null, responseType);

    Owner owner = responseEntity.getBody().getContent();
    assertEquals("George", owner.getFirstName());

    // more assertions

}

This approach is well described in the following article: Consuming Spring-hateoas Rest service using Spring RestTemplate and Super type tokens

Summary

With couple steps and the power of Spring Boot and Spring Data REST I created API for an existing PetClinic’s database. There is much more one can do with Spring Data REST (e.g. customization) and apart from rather poor documentation, comparing to other Spring projects, it seems like Spring Data REST may speedup the development significantly. In my opinion, this is a good project to look at when rapid prototyping is needed.

References

Rafal Borowiec

Software developer, Team Leader, Agile practitioner, occasional blogger, lecturer. Open Source enthusiast, quality oriented and open-minded.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Daniela Morais
Daniela Morais
8 years ago

I try follow this tutorial but I have a org.springframework.dao.InvalidDataAccessResourceUsageException
http://stackoverflow.com/questions/31521565/dbref-with-spring-data-rest

kiquetal
kiquetal
8 years ago

what IDE are you using?looks great!

Back to top button