Gregor Riegler

About Gregor Riegler

Gregor is a passionate software engineer and RESTafarian who loves to continuously improve. He is interested in modern web development, oo-design and extreme programming. He is also serious about clean code and TDD.

Spring Data REST in Action

What is spring-data-rest?

spring-data-rest, a recent addition to the spring-data project, is a framework that helps you expose your entities directly as RESTful webservice endpoints. Unlike rails, grails or roo it does not generate any code achieving this goal. spring data-rest supports JPA, MongoDB, JSR-303 validation, HAL and many more. It is really innovative and lets you setup your RESTful webservice within minutes. In this example i’ll give you a short overview of what spring-data-rest is capable of.

Initial Configuration

I’m gonna use the new Servlet 3 Java Web Configuration instead of an ancient web.xml. Nothing really special here.

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{AppConfiguration.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfiguration.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

WebAppInitializer.java on github

I am initializing hibernate as my database abstraction layer in the AppConfiguration class. I’m using an embedded database (hsql), since i want to keep this showcase simple stupid. Still, nothing special here.

@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
public class AppConfiguration {

    @Bean
    public DataSource dataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        return builder.setType(EmbeddedDatabaseType.HSQL).build();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setDatabase(Database.HSQL);
        vendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan(getClass().getPackage().getName());
        factory.setDataSource(dataSource());

        return factory;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager();
    }
}

AppConfiguration.java on github

Now to the application servlet configuration: WebConfiguration

@Configuration
public class WebConfiguration extends RepositoryRestMvcConfiguration {
}

WebConfiguration.java on github

Oh, well thats a bit short isnt it? Not a single line of code required for a complete setup. This is a really nice application of the convention over configuration paradigm. We can now start creating spring-data-jpa repositories as they will be exposed as RESTful resources automatically. And we can still add custom configuration to the WebConfiguration class if needed.

The Initialization was really short and easy. We didn’t have to code anything special. The only thing we did was setting up a database connection and hibernate, which is obviously inevitable. Now, that we have setup our “REST Servlet” and persistence, lets move on to the application itself, starting with the model.

The Model

I’ll keep it really simple creating only two related entities.
bookapi_model

@Entity
public class Book {

    @Id
    private String isbn;

    private String title;

    private String language;

    @ManyToMany
    private List<Author> authors;

}

Book.java on github

@Entity
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Integer id;

    private String name;

    @ManyToMany(mappedBy = "authors")
    private List<Book> books;

}

Author.java on github

To finally make the entities persistent and exposed as a RESTful webservice, we need spring-data repositories. A repository is basically a DAO. It offers CRUD functionality for our entities. spring-data takes away most of your programming effort creating such repositories. We just have to define an empty interface, spring-data does everything else out of the box. Still, it is easily customizable thanks to its design by convention over configuration!

The Actual Repositories

@RestResource(path = "books", rel = "books")
public interface BookRepository extends PagingAndSortingRepository<Book, Long> {
}

BookRepository.java on github

@RestResource(path = "authors", rel = "authors")
public interface AuthorRepository extends PagingAndSortingRepository<Author, Integer> {
}

AuthorRepository.java on github

Again, there is nearly no code needed. Even the @RestResource annotation could be left out. But if i did, the path and rel would be named after the entity, which i dont want. A REST resource that contains multiple children should be named plural though.

Accessing The Result

Our RESTful webservice is now ready for deployment. Once run, it lists all available resources on the root, so you can navigate from there.

GET http://localhost:8080/

{
  "links" : [ {
    "rel" : "books",
    "href" : "http://localhost:8080/books"
  }, {
    "rel" : "authors",
    "href" : "http://localhost:8080/authors"
  } ],
  "content" : [ ]
}

Fine! Now lets create an author and a book.

POST http://localhost:8080/authors

{"name":"Uncle Bob"}

Response

201 Created
Location: http://localhost:8080/authors/1

PUT http://localhost:8080/books/0132350882

{
  "title": "Clean Code",
  "authors": [
      {
          "rel": "authors",
          "href": "http://localhost:8080/authors/1"
      }
  ]
}

Response

201 Created

Noticed how i used PUT to create the book? This is because its id is the actual isbn. I have to tell the server which isbn to use since he cant guess it. I used POST for the author as his id is just an incremental number that is generated automatically. Also, i used a link to connect both, the book (/books/0132350882) and the author (/authors/1). This is basically what hypermedia is all about: Links are used for navigation and relations between entities.

Now, lets see if the book was created accordingly.

GET http://localhost:8080/books

{
  "links" : [ ],
  "content" : [ {
    "links" : [ {
      "rel" : "books.Book.authors",
      "href" : "http://localhost:8080/books/0132350882/authors"
    }, {
      "rel" : "self",
      "href" : "http://localhost:8080/books/0132350882"
    } ],
    "title" : "Clean Code"
  } ],
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 1
  }
}

Fine!

Here is an Integration Test, following these steps automatically. It is also available in the example on github.

public class BookApiIT {

    private final RestTemplate restTemplate = new RestTemplate();

    private final String authorsUrl = "http://localhost:8080/authors";
    private final String booksUrl = "http://localhost:8080/books";

    @Test
    public void testCreateBookWithAuthor() throws Exception {
        final URI authorUri = restTemplate.postForLocation(authorsUrl, sampleAuthor()); // create Author

        final URI bookUri = new URI(booksUrl + "/" + sampleBookIsbn);
        restTemplate.put(bookUri, sampleBook(authorUri.toString())); // create Book linked to Author

        Resource<Book> book = getBook(bookUri);
        assertNotNull(book);

        final URI authorsOfBookUri = new URI(book.getLink("books.Book.authors").getHref());
        Resource<List<Resource<Author>>> authors = getAuthors(authorsOfBookUri);
        assertNotNull(authors.getContent());
        assertFalse(authors.getContent().isEmpty()); // check if /books/0132350882/authors contains an author
    }

    private String sampleAuthor() {
        return "{\"name\":\"Robert C. Martin\"}";
    }

    private final String sampleBookIsbn = "0132350882";

    private String sampleBook(String authorUrl) {
        return "{\"title\":\"Clean Code\",\"authors\":[{\"rel\":\"authors\",\"href\":\"" + authorUrl + "\"}]}";
    }

    private Resource<Book> getBook(URI uri) {
        return restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<Resource<Book>>() {
        }).getBody();
    }

    private Resource<List<Resource<Author>>> getAuthors(URI uri) {
        return restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<Resource<List<Resource<Author>>>>() {
        }).getBody();
    }
}

BookApiIT.java on github

Conclusion

We have created a complete RESTful webservice without much coding effort. We just defined our entities and database connection. spring-data-rest stated that everything else is just boilerplate, and i agree.

To consume the webservices manually, consider the rest-shell. It is a command-shell making the navigation in your webservice as easy and fun as it could be. Here is a screenshot:
rest_shell
The complete example is available on my github
https://github.com/gregorriegler/babdev-spring/tree/master/spring-data-rest
https://github.com/gregorriegler/babdev-spring

 

Reference: Spring Data REST in Action from our JCG partner Gregor Riegler at the Be a better Developer blog.
Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

22 Responses to "Spring Data REST in Action"

  1. John Smith says:

    Your maven project doesn’t build. mvn package produces errors.

    • Gregor says:

      Thank you! i just found out that some really nice guy called Jay Sissom already fixed that.
      Thank you Jay, your pull request was merged!

      It should now build

      • Inshan says:

        Maven still cannot build. I am getting the following errors:
        Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.1.1:war (default-war) on project message-converter: Error assembling WAR: webxml attribute is required (or pre-existing WEB-INF/web.xml if executing in update mode) -> [Help 1]

        • Gregor says:

          web.xml was also missing for message-converter. i added it. you should now be able to package.

          but please, if you want to build the spring-data-rest package, just build the submodule not the main module. otherwise you are building both.

          greetings, gregor

          • Inshan says:

            Thanks. Builds fine. However when starting jetty (Jetty6 since we are using Java 6), the server starts up but it doesn’t seem to work well. In all, it would be great to have some steps as to how to get your code up and running soup to nuts.

  2. Edirne says:

    How i can fix if i use Turkish char set like; Ş, ı, ö, ğ ?

    • Gregor says:

      Hi Edirne,
      Im not familiar how spring-data-rest deals with unicode in particular, but what i do know is that there are several Points you need to check in order to get your characters to work.

      1.) Does your database support unicode characters? Did you use the right collation? Can you store a Ş in your database without spring-data?

      Often unicode problems are not caused by the framework but the database itself. So check that first!

      2.) Are the characters broken in Java?

      Debug a Request, follow the parsed body and check if they are already broken within java. If not, it might be an issue with the jdbc driver or something more database related.

      If it is already broken in Java you might check if you are using the right content-type in your HTTP Requests. Make sure that u are using an utf-8 content type like so:
      content=”application/json; charset=utf-8″

      3.) If all those points dont help, maybe your REST Client is causing broken characters. Some REST Clients are badly written and transfer iso-8859-1 Strings regardless of the charset that you are using.
      Thus, your characters break right in your REST Client.

      Make sure you try another rest client. curl should work without a problem.

  3. I’m attempting to startup your project and so far been unsuccessful.

    Do I need to download both of your projects? The REST project seems to complain about a parent pom.xml… I am unfamiliar with Maven and would really appreciate some assistance because your project is so far the simplest implementation I’ve found of a REST Data project..

    My own needs will require a MySQL instead of HSQL implementation as well, but I imagine it shouldn’t be as hard to figure out as Maven has been.

    • Gregor says:

      Hi Erik,

      Unfortunately, the examples are both inside a single multi-module-maven-project. Thus, i suggest you download the whole trunk. Maybe it’d be better if i separate those examples. I’ll do this as soon as possible.

      Greetings, Gregor

      • Erik says:

        Perhaps if you just explain what projects you’re leveraging I can try to whip up a gradle file.

        I see Hibernate, Spring-Data, but unsure what else. Trying to get this awesome code to run in a standalone manner via a Gradle build is my current goal.

      • erik says:

        After much research and toil, I got your project to compile in a standalone version. If you message me a good email I can zip it up to you.

        The only thing I need now, is to learn how to customize this example. I’m running Tomcat and want this service to deploy in a folder .. not root.

        I tried changing just WebAppInitializer.java , under the getServletMappings.. but it did not work.

        • Gregor says:

          Hi Erik,

          If you have a github account you can just make a pull request and change it. Otherwise, if you want to send me an e-mail, please goto my website. I have an obfuscated e-mail link in the right column

  4. Erik says:

    This is supposed to be deployed to a server, not run standalone.. correct? You kind of gloss over that in this part:

    “Our RESTful webservice is now ready for deployment. Once run, it lists all available resources on the root, so you can navigate from there.”

    I popped this into TomCat on my gradle based build of your project and it’s throwing errors.

  5. Erik says:

    In attempting to backtrack from my gradle build (it’s not deploying to TOMCAT properly), I thought I’d try building just your whole maven project..

    however your pom.xml has no build goals. I’m brand new to most of these tools and it would help if your POM had a goal to clarify what it’s building either a .war or .jar (.war in this case right?)

    [ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]

  6. Vidhya says:

    Nice article. i can build and deploy successfully.

    Could you please tell me where this data persist

  7. SDR says:

    thanks Gregor for writing up this tutorial, it has been very helpful to get started with Spring data rest.
    I have noticed one issue here: ID for book is String (isbn) yet, the BookRepository is declared as Long. Anyway, that was a easy fix. The part that I am having trouble with is adding book with author href. I am getting the following error:

    message: “Could not read JSON: (was java.lang.NullPointerException) (through reference chain: com.khan.sdr2.model.Book["authors"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.khan.sdr2.model.Book["authors"])”

    any idea ?

  8. Gowtham says:

    Hi, How to include validation for the fields using spring data rest. I have tried many ways all in vain. Any tutorials to do that?

  9. Alexis says:

    Hi is it possible to create a new author with his books in only ONE POST?

Leave a Reply


× 6 = twenty four



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books