Spring MVC RESTful Web Service Example with Spring Data for MongoDB, AngularJS, Bootstrap and Grid-UI

Spring MVC RESTful Web Service Example with Spring Data for MongoDB, AngularJS, Bootstrap and Grid-UI

In this post will show you how to build a RESTful web service using Spring MVC 4.0.6, Spring Data for MongoDB 1.6.1 so that we can integrate the web application with a highly efficient datastore (MongoDB 2.6). Additionally, this tutorial we will walk you through building the web service and NoSQL database backend and illustrate you how to implement CRUD (Create, Read, Update and Delete) operations.

To further enhance the user experience, we will use AngularJS, Grid-UI and BootStrap to create a visually appealing user interface that is highly efficient and easy to navigate and use.

For this post, we are building upon using our last post (Spring RESTful Web Service Example with JSON and Jackson using Spring Tool Suite) as a foundation.

We will be providing another tutorial with the focus on the GUI side and how we structured it using the various components including AngularJS, Bootstrap, CSS, JQuery, Spinner, Tree-Control and UI-Grid. However, for this tutorial we will focus on Spring MVC, Spring Data for persistence to MongoDB and RESTful API.

Our AngularJS Bootstrap Application

Getting Started using Spring Data

The primary goal of Spring Data is to make it easy to access both legacy relational databases in addition to new data technologies like NoSQL databases, map reduce frameworks and cloud based solutions. Spring Data for MongoDB is an umbrella project which aim to keep the consistent and familiar manner of the Spring based programming paradigm for new datastores.

In order to run this tutorial yourself, you will need the following:

Complete Project Overview

I have added the project overview to give you a full view of the structure and show you all files contained in this sample project.

angularjs grid-ui proj

RESTful Web Service End Points

#URIMethodDescription
1/rest/booksGETReturns a list of all the books available in MongoDB repository
2/rest/categoriesGETReturns a list of all the book categories in MongoDB repository
3/rest/getbookbyisbn?isbn13={book isbn13}GETRetrieve a book using ISBN13 as the key
4/rest/findbytitle?title={regex pattern}GETFind all books by title using regular expression
5/rest/findbycategory?category={book category}&title={regex pattern}GETFind all books in an given category and use title using regular expression, if title is provided as parameter.
6/rest/book/delete?isbn13={book isbn13}DELETEDelete a book in the MongoDB based on the ISBN13
7/rest/book/update?isbn13={book isbn13}PUTUpdate a book in the MongoDB based on the ISBN13
8/rest/book/add?isbn13={book isbn13}POSTInserts the book into the MongoDB datastore based on the contents of the AngularJS form

BookRepository Data Access Object (DAO) for MongoDB (BookRepository.java)

In this class you will notice two annotations being used. The first one, @Repository indicates that the class BookRepository fulfills the role of a Data Access Object of a repository. This class will handle all of the persistence of Book objects and database access for us.

The second annotation, @Autowired indicates that MongoTemplate is autowired from the Spring configuration, in this case our dispatcher-servlet.xml file.

package com.avaldes.dao;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;

import com.avaldes.model.Book;
 
@Repository
public class BookRepository {
  private static final Logger logger = LoggerFactory.getLogger(BookRepository.class);
  public static final String COLLECTION_NAME = "Book";
 
  @Autowired
  private MongoTemplate mongoTemplate;
 
  public void addBook(Book book) {
    if (!mongoTemplate.collectionExists(Book.class)) {
      mongoTemplate.createCollection(Book.class);
    }
    
    logger.info("Inside addBook()...");
    mongoTemplate.insert(book, COLLECTION_NAME);
  }
 
  public Book getBookByIsbn13(String isbn13) {
    return mongoTemplate.findOne(
      Query.query(Criteria.where("isbn13").is(isbn13)), Book.class, COLLECTION_NAME);
  }
 
  public List<Book> getBookByTitle(String title) {
    return mongoTemplate.find(
      Query.query(Criteria.where("title").regex(title, "i")), Book.class, COLLECTION_NAME);
  }
  
  public List<Book> getBookByCategory(String category) {
        return mongoTemplate.find(
          Query.query(Criteria.where("category").is(category)), Book.class, COLLECTION_NAME);
  }
  
  public List<Book> getBookByCategoryAndTitle(String category, String title) {
        return mongoTemplate.find(
          Query.query(Criteria.where("category").is(category).and("title").regex(title, "i")), Book.class, COLLECTION_NAME);
  }
  
  public List<Book> getAllBooks() {
    return mongoTemplate.findAll(Book.class, COLLECTION_NAME);
  }
 
  public Book deleteBook(String isbn13) {
    Book Book = mongoTemplate.findOne(
      Query.query(Criteria.where("isbn13").is(isbn13)), Book.class, COLLECTION_NAME);
    mongoTemplate.remove(Book, COLLECTION_NAME);
 
    return Book;
  }
 
  public Book updateBook(String isbn13, Book book) {
    Book myBook = mongoTemplate.findOne(
      Query.query(Criteria.where("isbn13").is(isbn13)), Book.class, COLLECTION_NAME);
    
    if (myBook != null) {
        logger.info("Inside updateBook(), updating record...");
        myBook.setIsbn13(book.getIsbn13());
        myBook.setTitle(book.getTitle());
        myBook.setAuthor(book.getAuthor());
        myBook.setPublisher(book.getPublisher());
        myBook.setLanguage(book.getLanguage());
        myBook.setIsbn10(book.getIsbn10());
        myBook.setDimensions(book.getDimensions());
        myBook.setShippingWeight(book.getShippingWeight());
        myBook.setDescription(book.getDescription());
        myBook.setPrice(book.getPrice());
        myBook.setCategory(book.getCategory());
        myBook.setQuantity(book.getQuantity());
        myBook.setActive(book.getActive());

        mongoTemplate.save(myBook, "Book");    
        return myBook;
        
    } else {
        logger.info("Inside updateBook(), unable to update record...");
        return null;
    } 
  }
}

CategoryRepository Data Access Object (DAO) for MongoDB (CategoryRepository.java)

package com.avaldes.dao;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;

import com.avaldes.model.Category;

@Repository
public class CategoryRepository {
    private static final Logger logger = LoggerFactory.getLogger(CategoryRepository.class);
    public static final String COLLECTION_NAME = "Category";
     
    @Autowired
    private MongoTemplate mongoTemplate;
    
    public Category getCategoryById(String id) {
        logger.info("Inside getCategoryById()...");
        return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)), Category.class, COLLECTION_NAME);
    }

    public Category getCategoryByText(String text) {
        logger.info("Inside getCategoryByText()...");
        return mongoTemplate.findOne(Query.query(Criteria.where("text").is(text)), Category.class, COLLECTION_NAME);
    }
    
    public List<Category> getAllCategories() {
        logger.info("Inside getAllCategories()...");
        return mongoTemplate.findAll(Category.class, COLLECTION_NAME);
    }
}

The Model Class for Book (Book.java)

The simple model is used as a basis for storing the fields into MongoDB as a document in the collection. This class contains two annotations. The first one, the @Document annotation identifies objects or entities that are going to be persisted to MongoDB. The next one, @Id is used to identify the field that will be used as an ID in MongoDB. This ID is labeled _id in MongoDB.

package com.avaldes.model;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.codehaus.jackson.annotate.JsonProperty;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
@XmlRootElement(name = "book")
public class Book  implements Serializable {
  private static final long serialVersionUID = -2387238728379L;
  
  @Id private String id;
  private String title;
  private String author;
  private String publisher;
  private String language;
  private String category;
  private String isbn10;
  private String isbn13;
  private String dimensions;
  private String shippingWeight;
  private String description;
  private float price;
  private int quantity;
  private boolean active;
  
  @XmlElement 
  @JsonProperty("id")
  public String getId() {
    return id;
  }
  
  public void setId(String id) {
    this.id = id;
  }
  
  @XmlElement 
  @JsonProperty("title")
  public String getTitle() {
    return title;
  }
  
  public void setTitle(String title) {
    this.title = title;
  }
  
  @XmlElement 
  @JsonProperty("author")
  public String getAuthor() {
    return author;
  }
  
  public void setAuthor(String author) {
    this.author = author;
  }
  
  @XmlElement 
  @JsonProperty("publisher")
  public String getPublisher() {
    return publisher;
  }
  
  public void setPublisher(String publisher) {
    this.publisher = publisher;
  }
  
  @XmlElement 
  @JsonProperty("language")
  public String getLanguage() {
    return language;
  }
  
  public void setLanguage(String language) {
    this.language = language;
  }
  
  @XmlElement 
  @JsonProperty("category")
  public String getCategory() {
    return category;
  }

  public void setCategory(String category) {
    this.category = category;
  }

  @XmlElement 
  @JsonProperty("isbn10")
  public String getIsbn10() {
    return isbn10;
  }
  
  public void setIsbn10(String isbn10) {
    this.isbn10 = isbn10;
  }
  
  @XmlElement 
  @JsonProperty("isbn13")
  public String getIsbn13() {
    return isbn13;
  }
  
  public void setIsbn13(String isbn13) {
    this.isbn13 = isbn13;
  }
  
  @XmlElement 
  @JsonProperty("dimensions")
  public String getDimensions() {
    return dimensions;
  }
  
  public void setDimensions(String dimensions) {
    this.dimensions = dimensions;
  }
  
  @XmlElement 
  @JsonProperty("shippingWeight")
  public String getShippingWeight() {
    return shippingWeight;
  }
  
  public void setShippingWeight(String shippingWeight) {
    this.shippingWeight = shippingWeight;
  }
  
  @XmlElement 
  @JsonProperty("description")
  public String getDescription() {
    return description;
  }
  
  public void setDescription(String description) {
    this.description = description;
  }

  @XmlElement 
  @JsonProperty("quantity")
  public int getQuantity() {
    return quantity;
  }

  public void setQuantity(int quantity) {
    this.quantity = quantity;
  }

  @XmlElement 
  @JsonProperty("active")
  public boolean getActive() {
    return active;
  }

  public void setActive(boolean active) {
    this.active = active;
  }

  @XmlElement 
  @JsonProperty("price")
  public float getPrice() {
    return price;
  }

  public void setPrice(float price) {
    this.price = price;
  }

  @Override
  public String toString() {
    return "Book [id=" + id + ", title=" + title + ", author=" + author
        + ", publisher=" + publisher + ", language=" + language
        + ", category=" + category + ", isbn10=" + isbn10 + ", isbn13="
        + isbn13 + ", dimensions=" + dimensions + ", shippingWeight="
        + shippingWeight + ", description=" + description + ", price="
        + price + ", quantity=" + quantity + ", active=" + active + "]";
  }
}

The Model Class for Category (Category.java)

package com.avaldes.model;

import java.io.Serializable;
import java.util.ArrayList;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.springframework.data.annotation.Id;

@XmlRootElement(name = "category")
public class Category implements Serializable {
    @Id private int id;
    private String text;
    private ArrayList<String> items= new ArrayList<String>();
    private static final long serialVersionUID = -1287129182981123L;
    
    @XmlElement
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @XmlElement
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    @XmlElement
    public ArrayList<String> getItems() {
        return items;
    }

    public void setItems(ArrayList<String> items) {
        this.items = items;
    }
}

RestResponse JSON Format

I have created the RestResponse class file in order to easily create the specific JSON format that I wanted for my application. It will be used to retrieve error messages from the back-end and display in modal windows in the front-end application GUI.

package com.avaldes.tutorial;

public class RestResponse {
    private boolean success;
      private String message;
     
      public RestResponse(boolean success, String message) {
        this.success = success;
        this.message = message;
      }
     
      public boolean isSuccess() {
        return success;
      }
      public void setSuccess(boolean success) {
        this.success = success;
      }
      public String getMessage() {
        return message;
      }
      public void setMessage(String message) {
        this.message = message;
      }
}

Controller Class (RestController.java)

Our RestController class is the main class that contains all web service mapping end points defined in our table above. The @Controller annotation indicates that this particular class is playing the role of a controller.

package com.avaldes.tutorial;
 
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.avaldes.dao.BookRepository;
import com.avaldes.dao.CategoryRepository;
import com.avaldes.model.Book;
import com.avaldes.model.Category;
 
/**
 * Handles requests for the application home page.
 */
@Controller
public class RestController {
 
  private static final Logger logger = LoggerFactory.getLogger(RestController.class);
  public static final String APPLICATION_JSON = "application/json";
  public static final String APPLICATION_XML = "application/xml";
  public static final String APPLICATION_HTML = "text/html";
 
  @Autowired 
  private BookRepository bookRepository; 

  @Autowired
  private CategoryRepository categoryRepository;

  /**
   * Simply selects the home view to render by returning its name.
 
   */
  @RequestMapping(value = "/", method = RequestMethod.GET, produces=APPLICATION_HTML)
  public @ResponseBody String status() {
    return "Default Status Message";
  }

  @RequestMapping(value="/books", method=RequestMethod.GET)
  public @ResponseBody List<Book> getAllBooks() {
    logger.info("Inside getAllBooks() method...");
 
    List<Book> allBooks = bookRepository.getAllBooks();
  
    return allBooks;
  }

  @RequestMapping(value="/categories", method=RequestMethod.GET)
  public @ResponseBody List<Category> getAllCategories() {
    logger.info("Inside getAllCategories() method...");
 
    List<Category> allCategories = categoryRepository.getAllCategories();
  
    return allCategories;
  } 
  
  @RequestMapping(value="/getbookbyisbn", method=RequestMethod.GET, produces={APPLICATION_JSON, APPLICATION_XML})
  public @ResponseBody Book getBookByIsbn13(@RequestParam("isbn13") String isbn13) {
    Book myBook = bookRepository.getBookByIsbn13(isbn13);
  
    if (myBook != null) {
      logger.info("Inside getBookByIsbn13, returned: " + myBook.toString());
    } else {
      logger.info("Inside getBookByIsbn13, isbn13: " + isbn13 + ", NOT FOUND!");
    }
  
    return myBook;
  }
 
  @RequestMapping(value="/findbytitle", method=RequestMethod.GET)
  public @ResponseBody List<Book> getBookByTitle(@RequestParam("title")String title) {
    List<Book> books = bookRepository.getBookByTitle(title);
  
    if (books == null) {
      logger.info("Inside getBookByTitle, title: " + title + ", NOT FOUND!");
    }
  
    return books;
  }
  
  @RequestMapping(value="/findbycategory", method=RequestMethod.GET)
  public @ResponseBody List<Book> getBookByCategory(@RequestParam("category") String category,
      @RequestParam(value="title", required=false) String title) {
    
    List<Book> books = null;
    
    if (category != null && title == null) {
      books = bookRepository.getBookByCategory(category);
    } else {
      books = bookRepository.getBookByCategoryAndTitle(category, title);
    }
  
    if (books == null) {
      logger.info("Inside getBookByCategory, category: " + category + ", NOT FOUND!");
    }
  
    return books;
  }
  

  @RequestMapping(value="/book/delete", method=RequestMethod.DELETE, 
            produces={APPLICATION_JSON, APPLICATION_XML})
  public @ResponseBody RestResponse deleteBookByIsbn13(@RequestParam("isbn13") String isbn13) {
    RestResponse response;
 
    Book myBook = bookRepository.deleteBook(isbn13);
 
    if (myBook != null) {
      logger.info("Inside deleteBookByIsbn13, deleted: " + myBook.toString());
      response = new RestResponse(true, "Successfully deleted Book: " + myBook.toString());
    } else {
      logger.info("Inside deleteBookByIsbn13, isbn13: " + isbn13 + ", NOT FOUND!");
      response = new RestResponse(false, "Failed to delete isbn13: " + isbn13);
    }
 
    return response;
  }
 
  @RequestMapping(value="/book/update", method=RequestMethod.PUT, 
        consumes={APPLICATION_JSON, APPLICATION_XML}, produces={APPLICATION_JSON, APPLICATION_XML})
  public @ResponseBody RestResponse updateBookByIsbn13(@RequestParam("isbn13") String isbn13, 
                              @RequestBody Book book) {
  RestResponse response;
 
  Book myBook = bookRepository.updateBook(isbn13, book);
 
    if (myBook != null) {
      logger.info("Inside updateBookByIsbn13, updated: " + myBook.toString());
      response = new RestResponse(true, "Successfully updated isbn13: " + myBook.toString());
    } else {
      logger.info("Inside updateBookByIsbn13, isbn13: " + isbn13 + ", NOT FOUND!");
      response = new RestResponse(false, "Failed to update isbn13: " + isbn13);
    }
 
    return response; 
  }
 
  @RequestMapping(value="/book/add", method=RequestMethod.POST, 
      consumes={APPLICATION_JSON, APPLICATION_XML}, produces={APPLICATION_JSON, APPLICATION_XML})
  
  public @ResponseBody RestResponse addBook(@RequestParam("isbn13") String isbn13, @RequestBody Book book) {
    RestResponse response;
 
  logger.info("Inside addBook, model attribute: " + book.toString());
  
  if (isbn13 == null) {
    response = new RestResponse(false, "ISBN13 may not be null.");
    return response;
  }
  
  if (isbn13 != null && isbn13.isEmpty()) {
    response = new RestResponse(false, "ISBN13 may not be empty.");
    return response;
  } 

  Book myBook = getBookByIsbn13(isbn13);
  if (myBook != null) {
    if (myBook.getIsbn13() != null && myBook.getIsbn13().equalsIgnoreCase(isbn13)) {
      response = new RestResponse(false, "ISBN13 already exists in the system.");
      return response;
    }
  }
  
  if (book.getIsbn13() != null && book.getIsbn13().length() > 0) {
      logger.info("Inside addBook, adding: " + book.toString());
      bookRepository.addBook(book);
      response = new RestResponse(true, "Successfully added Book: " + book.getIsbn13());
    } else {
      logger.info("Failed to insert...");
      response = new RestResponse(false, "Failed to insert...");
    }
 
    return response;
  }
}

Web Deployment Descriptor (web.xml)

Our web.xml is quite straight forward. Here we define our DispatcherServlet servlet, define our servlet’s application context and define what our URL pattern is going to be for the dispatcher.

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5"
  xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name>AngularUIGrid</display-name>
  
  <!-- The context params that are read by Log4jConfigListener -->
  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.xml</param-value> 
  </context-param>

  <listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>
  
  <!-- The context params that are read by ContextLoaderListener  -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/root-context.xml</param-value>
  </context-param>
  
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Configure Spring Web DispatcherServlet (dispatcher-servlet.xml)

Modify the dispatcher-servlet.xml and add the necessary MongoDB configurations. You will notice that I have added MongoTemplate which is used for the mongo operations and MongoFactoryBean which creates the mongo instance to our dispatcher-servlet.xml. MongoTemplate is configured to use the database settings via MongoFactoryBean.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
 
  <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
 
  <!-- Enables the Spring MVC @Controller programming model -->
  <!-- <beans:bean annotation-driven /> -->
  <annotation-driven />
 
  <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
  <resources mapping="/resources/**" location="/resources/" />
 
  <!-- Define the MongoTemplate which handles connectivity with MongoDB -->
  <beans:bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <beans:constructor-arg name="mongo" ref="mongo" />
    <beans:constructor-arg name="databaseName" value="datastore" />
  </beans:bean>
  
  <!-- Factory bean that creates the MongoDB instance -->
  <beans:bean id="mongo" class="org.springframework.data.mongodb.core.MongoFactoryBean">
    <beans:property name="host" value="localhost"/>
  </beans:bean>
 
  <!-- Use this post processor to translate any MongoExceptions thrown in @Repository annotated classes -->
  <beans:bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
 
  <beans:bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <beans:property name="messageConverters">
        <beans:list>
            <beans:bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
        </beans:list>
    </beans:property>
</beans:bean>

<context:component-scan base-package="com.avaldes" />
 
</beans:beans>

JSON Books Documents

I am including JSON documents in the event that you may want to pre-populate your MongoDB collection.

{
    "_id" : "978-0544272996",
    "title" : "What If?: Serious Scientific Answers to Absurd Hypothetical Questions",
    "author" : "Randall Munroe",
    "publisher" : "Houghton Mifflin Harcourt; First Edition edition (September 2, 2014)",
    "isbn10" : "0544272994",
    "isbn13" : "978-0544272996",
    "language" : "english",
    "category" : "16000",
    "dimensions" : "7 x 1.2 x 9 inches",
    "shippingWeight" : "1.8 pounds",
    "description" : "Millions of people visit xkcd.com each week to read Randall Munroe’s iconic webcomic. His stick-figure drawings about science, technology, language, and love have a large and passionate following.  Fans of xkcd ask Munroe a lot of strange questions. What if you tried to hit a baseball pitched at 90 percent the speed of light? How fast can you hit a speed bump while driving and live? If there was a robot apocalypse, how long would humanity last?  In pursuit of answers, Munroe runs computer simulations, pores over stacks of declassified military research memos, solves differential equations, and consults with nuclear reactor operators. His responses are masterpieces of clarity and hilarity, complemented by signature xkcd comics. They often predict the complete annihilation of humankind, or at least a really big explosion.",
    "price" : 12.57,
    "quantity" : 32,
    "active" : true
}

{
    "_id" : "978-0805098105",
    "title" : "Missing Microbes: How the Overuse of Antibiotics Is Fueling Our Modern Plagues",
    "author" : "Martin J. Blaser",
    "publisher" : "Henry Holt and Co.; 1 edition (April 8, 2014)",
    "isbn10" : "0805098100",
    "isbn13" : "978-0805098105",
    "language" : "italian",
    "category" : "16000",
    "dimensions" : "6.3 x 1 x 9.4 inches",
    "shippingWeight" : "1 pounds",
    "description" : "A critically important and startling look at the harmful effects of overusing antibiotics, from the field's leading expert Tracing one scientist's journey toward understanding the crucial importance of the microbiome, this revolutionary book will take readers to the forefront of trail-blazing research while revealing the damage that overuse of antibiotics is doing to our health: contributing to the rise of obesity, asthma, diabetes, and certain forms of cancer. In Missing Microbes, Dr. Martin Blaser invites us into the wilds of the human microbiome where for hundreds of thousands of years bacterial and human cells have existed in a peaceful symbiosis that is responsible for the health and equilibrium of our body. Now, this invisible eden is being irrevocably damaged by some of our most revered medical advances--antibiotics--threatening the extinction of our irreplaceable microbes with terrible health consequences. Taking us into both the lab and deep into the fields where these troubling effects can be witnessed firsthand, Blaser not only provides cutting edge evidence for the adverse effects of antibiotics, he tells us what we can do to avoid even more catastrophic health problems in the future.",
    "price" : 21.03,
    "quantity" : 3,
    "active" : false
}

{
    "_id" : "978-1476708690",
    "title" : "The Innovators: How a Group of Hackers, Geniuses, and Geeks Created the Digital Revolution",
    "author" : "Walter Isaacson",
    "publisher" : "Simon & Schuster; 1St Edition edition (October 7, 2014)",
    "isbn10" : "147670869X",
    "isbn13" : "978-1476708690",
    "language" : "english",
    "category" : "16000",
    "dimensions" : "6.1 x 1.6 x 9.2 inches",
    "shippingWeight" : "1.8 pounds",
    "description" : "Following his blockbuster biography of Steve Jobs, The Innovators is Walter Isaacson’s revealing story of the people who created the computer and the Internet. It is destined to be the standard history of the digital revolution and an indispensable guide to how innovation really happens.  What were the talents that allowed certain inventors and entrepreneurs to turn their visionary ideas into disruptive realities? What led to their creative leaps? Why did some succeed and others fail?  In his masterly saga, Isaacson begins with Ada Lovelace, Lord Byron’s daughter, who pioneered computer programming in the 1840s. He explores the fascinating personalities that created our current digital revolution, such as Vannevar Bush, Alan Turing, John von Neumann, J.C.R. Licklider, Doug Engelbart, Robert Noyce, Bill Gates, Steve Wozniak, Steve Jobs, Tim Berners-Lee, and Larry Page",
    "price" : 23.84,
    "quantity" : 7,
    "active" : true
}

{
    "_id" : "978-1476770383",
    "title" : "Revival: A Novel",
    "author" : "Stephen King",
    "publisher" : "Scribner; 1st edition (November 11, 2014)",
    "isbn10" : "1476770387",
    "isbn13" : "978-1476770383",
    "language" : "english",
    "category" : "13000",
    "dimensions" : "6.1 x 1.4 x 9.2 inches",
    "shippingWeight" : "1.6 pounds",
    "description" : "A dark and electrifying novel about addiction, fanaticism, and what might exist on the other side of life.  In a small New England town, over half a century ago, a shadow falls over a small boy playing with his toy soldiers. Jamie Morton looks up to see a striking man, the new minister. Charles Jacobs, along with his beautiful wife, will transform the local church. The men and boys are all a bit in love with Mrs. Jacobs; the women and girls feel the same about Reverend Jacobs—including Jamie’s mother and beloved sister, Claire. With Jamie, the Reverend shares a deeper bond based on a secret obsession. When tragedy strikes the Jacobs family, this charismatic preacher curses God, mocks all religious belief, and is banished from the shocked town.  Jamie has demons of his own. Wed to his guitar from the age of thirteen, he plays in bands across the country, living the nomadic lifestyle of bar-band rock and roll while fleeing from his family’s horrific loss. In his mid-thirties—addicted to heroin, stranded, desperate—Jamie meets Charles Jacobs again, with profound consequences for both men. Their bond becomes a pact beyond even the Devil’s devising, and Jamie discovers that revival has many meanings.",
    "price" : 14.71,
    "quantity" : 12,
    "active" : true
}

JSON Category Documents

{ _id: 0, text: "All Books", items: [] }
{ _id: 1000, text: "Arts & Photography", items: [] }
{ _id: 2000, text: "Audiobooks", items: [] }
{ _id: 3000, text: "Biographies & Memoirs", items: [] }
{ _id: 4000, text: "Business & Investing", items: [] }
{ _id: 5000, text: "Children's Books", items: [] }
{ _id: 6000, text: "Comics & Graphic Novels", items: [] }
{ _id: 7000, text: "Cookbooks & Food Writing", items: [] }
{ _id: 8000, text: "Crafts, Home & Garden", items: [] }
{ _id: 9000, text: "Gift Picks", items: ["90100", "90200", "90300", "90400", "90500", "90600", "90700", "90800", "90900", "91000"] }
{ _id: 10000, text: "History", items: [] }
{ _id: 11000, text: "Humor & Entertainment", items: [] }
{ _id: 12000, text: "Literature & Fiction", items: [] }
{ _id: 13000, text: "Mystery, Thriller & Suspense", items: [] }
{ _id: 14000, text: "Nonfiction", items: [] }
{ _id: 15000, text: "Romance", items: [] }
{ _id: 16000, text: "Science", items: [] }
{ _id: 17000, text: "Science Fiction & Fantasy", items: [] }
{ _id: 18000, text: "Sports & Outdoors", items: [] }
{ _id: 19000, text: "Teen & Young Adult", items: [] }
{ _id: 90100, text: "Little Bookworms", items: [] }
{ _id: 90200, text: "Fun & Quirky", items: [] }
{ _id: 90300, text: "Eat, Drink, Read", items: [] }
{ _id: 90400, text: "For the Young at Heart", items: [] }
{ _id: 90500, text: "Fantastic Fiction", items: [] }
{ _id: 90600, text: "Nothing But the Truth", items: [] }
{ _id: 90700, text: "Eye Candy", items: [] }
{ _id: 90800, text: "Cops & Crooks", items: [] }
{ _id: 90900, text: "Secrets of Success", items: [] }
{ _id: 91000, text: "Design, Construct, Create", items: [] }

Let’s Test out the Spring MVC REST API calls

For this server-side test, I will be using I’m Only Resting application from Swen Sen Software.

Let’s View the AngularJS/Bootstrap GUI Screens

As you can see from the following screenshots, the UI is quite appealing in its design.

Download the Code

That’s It!

I hope you enjoyed this tutorial. It was certainly a lot of fun putting it together and testing it out. Please continue to share the love and like us so that we can continue bringing you quality tutorials. Happy Coding!!!

spring mvc angular bootstrap

Please Share Us on Social Media

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply to Amaury Valdes Cancel reply

Your email address will not be published. Required fields are marked *