Monday, November 26, 2012

Book Review: Spring Data

I'm currently reviewing the book Spring Data from Packt Publishing. You can find the book at http://www.packtpub.com/spring-data/book.

Overview
This book is about Spring Data JPA and Spring Data Redis

  • Implement JPA repositories with lesser code
  • Includes functional sample projects that demonstrate the described concepts in action and help you start experimenting right away
  • Provides step-by-step instructions and a lot of code examples that are easy to follow and help you to get started from page one


Full review
My initial impression of this book is that it's too short and lacks full coverage because it only focuses on Spring Data JPA and Spring Data Redis. We all know how big the Spring Data umbrella projects are. But it turns out this is the strength of the book. By focusing on a subset of Spring Data umbrella projects, it's able to focus better on what matters most.

As I read the book, I slowly realized that this book is a gem. If you need a solid understanding of Spring Data JPA, read this book. It tells you step-by-step all the possible query technologies, usage patterns, and their pros and cons. The book gradually prepares the reader to the value of Spring Data.

The Spring Data JPA coverage is quite extensive. It teaches you how to download and install the necessary libraries. Configuration is based on programmatic configuration instead of the usual XML configuration files. I think this is good but also bad. It would be great if the book offers sample configuration both in XML and Java-based config. Since most users are familiar with XML configuration, translating from Java-based config would require extra effort to comprehend for most Spring users. Anyway, that's a minor weakness that we can live-up with.

The book is successful in demonstrating how to provide CRUD support through Spring Data JPA and how to implement your own custom repository. There are various way to perform queries in Spring Data JPA, and I think the book has managed to cover all of them, including QueryDSL.

The book's coverage on Spring Data Redis is extensive. It covers installation and configuration, connector types, Redis data structures, and of course, Spring Data support for Redis. The book teaches how to save relational data and perform CRUD operations in a NoSQL manner. It also covers messaging and caching support with Redis. Overall it's a pleasant read. It's interesting how the book has smoothly transitioned from Spring Data JPA to Redis.

Overall, Spring Data from Packt Publishing is a solid book that I recommend to everyone to read.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Book Review: Spring Data ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Tuesday, November 20, 2012

Spring MVC 3.2 with Spring Data Rest (Part 1)

In this tutorial, we will update an existing Spring MVC app to Spring MVC 3.2 and add RESTful endpoints using Spring Data Rest. The goal is to demonstrate how we can implement HATEOAS methodology using Spring.

Before we start, please take some time to review our existing application: Spring MVC 3.1, jqGrid, and Spring Data JPA Integration Guide. If all you need is a pure Spring Data Rest application, please visit the official Spring Data Rest starter web application.

Table of Contents

Part 1: Configuration
  • Update the pom.xml
  • Update the web.xml
  • Spring Configurations
  • Repositories
Part 2: Running the application

Dependencies

  • Spring core 3.2.0.RC1
  • Spring Data Rest 1.0.0.RC3
  • Spring Data JPA 1.1.0.RELEASE
  • jQuery 1.6.4
  • jqGrid 4.3.1
  • See pom.xml for details

Github

To access the source code, please visit the project's Github repository (click here)

Update the pom.xml

Here are the changes that we need to do:
  • Update the Spring core version
  • Update the Spring Data JPA version
  • Add Spring Data Rest dependency



Note: I also added the profiles section at the end of the pom.xml, so that we can expose the repositories in a clean manner.

Update the web.xml

Here are the changes that we need to do:
  • Update the web-app version to 2.5 (optional)
  • Update the display-name (optional)
  • Add Spring Data Rest servlet



Spring Configurations

We need to do three tasks:
  • Update the database name
  • Create a spring-data-rest.xml
  • Update the applicationContext.xml

Update the database name

Open the spring.properties under WEB-INF folder, and update it as follows:


In the original application, the declared database name is spring_jqgrid_tutorial, let's update it to spring_data_rest_tutorial (though this is really not needed).

Create a spring-data-rest.xml



Update the applicationContext.xml

There's not much update here. We just need to import the spring-data-rest.xml as follows:


Repositories

We need to do two tasks:
  • Update UserRepository
  • Create a new repository RoleRepository

The only update here is the addition of the annotation @Param to the UserRepository. This is required so that we can expose the parameters in the search queries.


We need to create a new repository for the Role domain so that we can expose it as RESTful endpoint:


Next

In the next section, we will build and run the application and test our RESTful endpoints. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3.2 with Spring Data Rest (Part 1) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring MVC 3.2 with Spring Data Rest (Part 2)

Review

In the previous section, we have updated our app's configuration, so that our repositories are exposed as RESTful endpoints. We've also applied the HATEOAS methodology which "serves to decouple client and server in a way that allows the server to evolve functionality independently" (Wikipedia). In this section, we will build and run the application using Maven, demonstrate how to import the project in Eclipse, and run a series of manual tests to examine the RESTful endpoints.

Table of Contents

Part 1: Configuration
  • Update the pom.xml
  • Update the web.xml
  • Update Spring configurations
  • Update the repositories
Part 2: Running the application

What is HATEOAS?

HATEOAS, an abbreviation for Hypermedia as the Engine of Application State, is a constraint of the REST application architecture that distinguishes it from most other network application architectures. The principle is that a client interacts with a network application entirely through hypermedia provided dynamically by application servers. A REST client needs no prior knowledge about how to interact with any particular application or server beyond a generic understanding of hypermedia. Contrast this with e.g. a service-oriented architecture (SOA), where clients and servers interact through a fixed interface shared through documentation or an interface description language (IDL).

The HATEOAS constraint serves to decouple client and server in a way that allows the server to evolve functionality independently.

Source: http://en.wikipedia.org/wiki/HATEOAS


Running the Application

Access the source code

To download the source code, please visit the project's Github repository (click here)

Preparing the data source

  1. Run MySQL (install one if you don't have one yet)
  2. Create a new database:
    spring_data_rest_tutorial
  3. Import the following file which is included in the source code under the src/main/resources folder:
    spring_data_rest_tutorial.sql

Building with Maven

  1. Ensure Maven is installed
  2. Open a command window (Windows) or a terminal (Linux/Mac)
  3. Run the following command:
    mvn tomcat:run
  4. You should see the following output:
    [INFO] Scanning for projects...
    [INFO] Searching repository for plugin with prefix: 'tomcat'.
    [INFO]                                                                         
    [INFO] --------------------------------------------------------------
    [INFO] Building spring-data-rest-tutorial Maven Webapp 0.0.1-SNAPSHOT
    [INFO] --------------------------------------------------------------
    [INFO]
    [INFO] Preparing tomcat:run
    [INFO] [apt:process {execution: default}]
    [INFO] [resources:resources {execution: default-resources}]
    [INFO] [tomcat:run {execution: default-cli}]
    [INFO] Running war on http://localhost:8080/spring-data-rest-tutorial
    Nov 20, 2012 8:01:45 PM org.apache.catalina.startup.Embedded start
    INFO: Starting tomcat server
    Nov 20, 2012 8:01:45 PM org.apache.catalina.core.StandardEngine start
    INFO: Starting Servlet Engine: Apache Tomcat/6.0.29
    Nov 20, 2012 8:01:46 PM org.apache.catalina.core.ApplicationContext log
    INFO: Initializing Spring root WebApplicationContext
    Nov 20, 2012 8:02:01 PM org.apache.catalina.core.ApplicationContext log
    INFO: Initializing Spring FrameworkServlet 'rest-exporter'
    Nov 20, 2012 8:02:03 PM org.apache.coyote.http11.Http11Protocol init
    INFO: Initializing Coyote HTTP/1.1 on http-8080
    Nov 20, 2012 8:02:03 PM org.apache.coyote.http11.Http11Protocol start
    INFO: Starting Coyote HTTP/1.1 on http-8080
    
  5. Note: If the project will not build due to missing repositories, please enable the repositories section in the pom.xml!

Access the grid page

This displays a grid that allows us to experiment with the data visually.
  1. Follow the steps with Building with Maven
  2. Open a browser
  3. Enter the following URL (8080 is the default port for Tomcat):
    http://localhost:8080/spring-data-rest-tutorial/

Access the RESTful entry endpoint

We've declared entry endpoint in spring-data-rest.xml and web.xml files.
  1. Follow the steps with Building with Maven
  2. Open a browser
  3. Enter the following URL:
    http://localhost:8080/spring-data-rest-tutorial/api/

    This gives the following result:
    {
      "links" : [ {
        "rel" : "role",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/role"
      }, {
        "rel" : "user",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user"
      } ],
      "content" : [ ]
    }
    


Access the User endpoint

  1. Enter the following URL:
    http://localhost:8080/spring-data-rest-tutorial/api/user

    This gives the following result:
    {
      "links" : [ {
        "rel" : "user.search",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search"
      } ],
      "content" : [ {
        "links" : [ {
          "rel" : "self",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1"
        }, {
          "rel" : "user.User.role",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1/role"
        } ],
        "lastName" : "Smith",
        "username" : "john",
        "firstName" : "John",
        "password" : "21232f297a57a5a743894a0e4a801fc3"
      }, {
        "links" : [ {
          "rel" : "self",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/2"
        }, {
          "rel" : "user.User.role",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/2/role"
        } ],
        "lastName" : "Adams",
        "username" : "jane",
        "firstName" : "Jane",
        "password" : "ee11cbb19052e40b07aac0ca060c23ee"
      },
      ... (TRUNCATED)
      ... (TRUNCATED)
      ... (TRUNCATED)
      {
        "links" : [ {
          "rel" : "self",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/13"
        }, {
          "rel" : "user.User.role",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/13/role"
        } ],
        "lastName" : "Zeigler",
        "username" : "edward",
        "firstName" : "Edward",
        "password" : "mncmksk"
      } ],
      "page" : {
        "size" : 20,
        "totalElements" : 13,
        "totalPages" : 1,
        "number" : 1
      }
    }
    


Access the User endpoint with a limit

  1. Enter the following URL:
    http://localhost:8080/spring-data-rest-tutorial/api/user?limit=2

    This gives the following result:
    {
      "links" : [ {
        "rel" : "user.next",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user?page=2&limit=2"
      }, {
        "rel" : "user.search",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search"
      } ],
      "content" : [ {
        "links" : [ {
          "rel" : "self",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1"
        }, {
          "rel" : "user.User.role",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1/role"
        } ],
        "lastName" : "Smith",
        "username" : "john",
        "firstName" : "John",
        "password" : "21232f297a57a5a743894a0e4a801fc3"
      }, {
        "links" : [ {
          "rel" : "self",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/2"
        }, {
          "rel" : "user.User.role",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/2/role"
        } ],
        "lastName" : "Adams",
        "username" : "jane",
        "firstName" : "Jane",
        "password" : "ee11cbb19052e40b07aac0ca060c23ee"
      } ],
      "page" : {
        "size" : 2,
        "totalElements" : 12,
        "totalPages" : 6,
        "number" : 1
      }
    }
    

    Notice the result also provided the endpoint for search and each person's record.

Let's do a search

  1. Open a browser
  2. Enter the following URL:
    http://localhost:8080/spring-data-rest-tutorial/api/user/search

    This gives the following result:
    {
      "links" : [ {
        "rel" : "user.findByFirstNameLike",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByFirstNameLike"
      }, {
        "rel" : "user.findByUsernameLike",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByUsernameLike"
      }, {
        "rel" : "user.findByFirstNameLikeAndLastNameLike",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByFirstNameLikeAndLastNameLike"
      }, {
        "rel" : "user.findByUsername",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByUsername"
      }, {
        "rel" : "user.findByLastNameLike",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByLastNameLike"
      }, {
        "rel" : "user.findByRole",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByRole"
      } ],
      "content" : [ ]
    }
    

    This exposes all the queries we declared in the UserRepository interface.
  3. Let's try searching by username.
  4. Enter the following URL:
    http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByUsername?username=john

    This gives the following result:
    {
      "links" : [ ],
      "content" : [ {
        "links" : [ {
          "rel" : "self",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1"
        }, {
          "rel" : "user.User.role",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1/role"
        } ],
        "lastName" : "Smith",
        "username" : "john",
        "firstName" : "John",
        "password" : "21232f297a57a5a743894a0e4a801fc3"
      } ]
    }
    



Delete a record

You are required to have curl installed.
  1. Open a command line.
  2. Enter the following command:
    curl -v -X DELETE http://localhost:8080/spring-data-rest-tutorial/api/user/3

    This gives the following output:
    * About to connect() to localhost port 8080 (#0)
    *   Trying 127.0.0.1... connected
    * Connected to localhost (127.0.0.1) port 8080 (#0)
    > DELETE /spring-data-rest-tutorial/api/user/3 HTTP/1.1
    > User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3
    > Host: localhost:8080
    > Accept: */*
    > 
    < HTTP/1.1 204 No Content
    < Server: Apache-Coyote/1.1
    < Date: Mon, 19 Nov 2012 14:32:05 GMT
    < 
    * Connection #0 to host localhost left intact
    * Closing connection #0
    
    If you check the records, notice that User with id 3 has been deleted.

Add a record

You are required to have curl installed.
  1. Open a command line.
  2. Enter the following command:
    curl -v -d '{"username":"homer", "firstName":"Homer", "lastName":"Simpson", "password":"12345678"}' -H "Content-Type: application/json" http://localhost:8080/spring-data-rest-tutorial/api/user

    Under Windows use the following command instead:
    curl -v -d "{\"username\":\"homer\", \"firstName\":\"Homer\", \"lastName\":\"Simpson\", \"password\":\"12345678\"}" -H "Content-Type: application/json" http://localhost:8080/spring-data-rest-tutorial/api/user
    

    This adds a User record and gives the following output:
    * About to connect() to localhost port 8080 (#0)
    *   Trying 127.0.0.1... connected
    > POST /spring-data-rest-tutorial/api/user HTTP/1.1
    > User-Agent: curl/7.22.0 (i386-pc-win32) libcurl/7.22.0 OpenSSL/1.0.0e zlib/1.2
    .5
    > Host: localhost:8080
    > Accept: */*
    > Content-Type: application/json
    > Content-Length: 86
    >
    * upload completely sent off: 86out of 86 bytes
    < HTTP/1.1 201 Created
    < Server: Apache-Coyote/1.1
    < Location: http://localhost:8080/spring-data-rest-tutorial/api/user/14
    < Content-Type: application/octet-stream
    < Content-Length: 0
    < Date: Tue, 20 Nov 2012 04:58:56 GMT
    <
    * Connection #0 to host localhost left intact
    * Closing connection #0
    
  3. However, a Role has not been associated with this User record. We have to manually create and associate one. Open a command line.
  4. Enter the following command:
    curl -v -d '{"role":"1","user": {"rel": "user","href": "http://localhost:8080/spring-data-rest-tutorial/api/user/14"}}' -H "Content-Type: application/json" http://localhost:8080/spring-data-rest-tutorial/api/role

    Under Windows use the following command instead:
    curl -v -d "{\"role\":\"1\",\"user\": {\"rel\": \"user\",\"href\": \"http://localhost:8080/spring-data-rest-tutorial/api/user/14\"}}" -H "Content-Type: application/json" http://localhost:8080/spring-data-rest-tutorial/api/role
    

    This adds a Role record and gives the following output:
    * About to connect() to localhost port 8080 (#0)
    *   Trying 127.0.0.1... connected
    > POST /spring-data-rest-tutorial/api/role HTTP/1.1
    > User-Agent: curl/7.22.0 (i386-pc-win32) libcurl/7.22.0 OpenSSL/1.0.0e zlib/1.2
    .5
    > Host: localhost:8080
    > Accept: */*
    > Content-Type: application/json
    > Content-Length: 106
    >
    * upload completely sent off: 106out of 106 bytes
    < HTTP/1.1 201 Created
    < Server: Apache-Coyote/1.1
    < Location: http://localhost:8080/spring-data-rest-tutorial/api/role/14
    < Content-Type: application/octet-stream
    < Content-Length: 0
    < Date: Tue, 20 Nov 2012 04:59:57 GMT
    <
    * Connection #0 to host localhost left intact
    * Closing connection #0
    
  5. Examine the output and we should see the new record:
    http://localhost:8080/spring-data-rest-tutorial/api/user/14
    
    {
        "links" : [ {
          "rel" : "self",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/14"
        }, {
          "rel" : "user.User.role",
          "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/14/role"
        } ],
        "lastName" : "Simpson",
        "username" : "homer",
        "firstName" : "Homer",
        "password" : "12345678"
      }
    
    http://localhost:8080/spring-data-rest-tutorial/api/user/14/role
    {
      "links" : [ {
        "rel" : "self",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/role/14"
      }, {
        "rel" : "role.Role.user",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/role/14/user"
      }, {
        "rel" : "user.User.role",
        "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/14/role"
      } ],
      "role" : 1
    }
    
  6. Examine the grid and we shall see the new record as well:


Note: You can do more with Spring Data Rest. I suggest reading the docs further for more info.

Import the project in Eclipse

  1. Ensure Maven is installed
  2. Open a command window (Windows) or a terminal (Linux/Mac)
  3. Run the following command:
    mvn eclipse:eclipse -Dwtpversion=2.0
  4. You should see the following output:
    [INFO] Scanning for projects...
    [INFO]                                                                         
    [INFO] --------------------------------------------------
    [INFO] Building spring-data-rest-tutorial Maven Webapp 0.0.1-SNAPSHOT
    [INFO] ---------------------------------------------------
    [INFO] 
    [INFO] Adding support for WTP version 2.0.
    [INFO] 
    [INFO] --------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] --------------------------------------------------
    
    This command will add the following files to your project:
    .classpath
    .project
    .settings
    target
    You may have to enable "show hidden files" in your file explorer to view them
  5. Open Eclipse and import the project

Conclusion

That's it! We've have successfully updated our Spring MVC application and exposed our repositories as RESTful endpoints using Spring Data Rest. We've also demonstrated how to access our application using pure hyperlinks with HATEOAS methodology.

I hope you've enjoyed this tutorial. Don't forget to check my other tutorials at the Tutorials section.

Revision History


Revision Date Description
1 Nov 20 2012 Uploaded tutorial and Github repository

StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3.2 with Spring Data Rest (Part 2) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share