Sunday, December 9, 2018

Spring Boot REST API CRUD operations with MySQL with Spring Data

In this post I'm going to setup REST API project with Spring Boot and this will use mysql data base with Spring Data as persistence API.

Prerequisites 
  1. You should have install java 1.8 or above.
  2. You should have Eclipse installed in your PC.
  3. Your PC should setup Maven installed and configured.


Lest Create the project

First of all open Eclipse IDE and go to File - > New -> Maven Project. Click on create simple project and press next. Then provide your project details. In my example im giving group Id as "com.nirmal.springbootrest" and artifact Id as "RestApiSpringBoot". Then click finish.


Lets start the configuration of the project. First of all make sure to change run time JRE in to JDK. You can do it by right click on the project and go to Build path -> Configure Build path... then go to libraries and select JDK instead of JRE. 

Spring Boot configurations

First of all lets add the dependencies in to POM file. Since Spring Boot remove complexity of project setup we just need to add Spring Boot  references dependencies in to pom and do the required configurations based on application.properties file.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.nirmal.springbootrest</groupId>
  <artifactId>RestApiSpringBoot</artifactId>
  <version>0.0.1-SNAPSHOT</version>

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- Runtime -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
    
</project>

Then lets add our Spring Boot initializer class in to the project. Right click on project and go to New -> Class and provide class name as "AppInitializer" and package I would give as "com.nirmal.springbootrest".

I will add following content in to out initializer class.


package com.nirmal.springbootrest;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

/**
 * Spring Boot initialization class of the ResrApiSpringBoot project
 * 
 * @author Nirmal Balasooriya
 *
 */

@ComponentScan({ "com.nirmal.springbootrest", "com.nirmal.springbootres.controller" })
@EnableJpaRepositories("com.nirmal.springbootrest")
@SpringBootApplication(scanBasePackages = { "com.nirmal.springbootres.controller" })
public class AppInitializer extends SpringBootServletInitializer {

@Autowired
DataSource dataSource;

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AppInitializer.class);
}

public static void main(String[] args) {
SpringApplication.run(AppInitializer.class, args);
}
}



Then lets add required configurations based on application.properties file. Right click on src/main/resources add go to New -> Other select file and provide file name as "application.properties".

#==== connect to mysql ======#
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/sample?useSSL=false
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect


spring.jpa.properties.hibernate.show_sql=false
spring.jpa.properties.hibernate.use_sql_comments=false
spring.jpa.properties.hibernate.format_sql=true

#==== Logging configurations ======#
logging.level.root=WARN,INFO,ERROR
logging.level.com.baeldung=TRACE


Here I will user my database as sample and it would be mysql database. Now you can right click on project and perform maven install by click on Run as -> Maven Install as show in bellow figure.



Then lets create mysql database call "sample" then lets see whether our application start successfully by run the AppInitializer class as java application. If its started successful you should be able to see similar out put as bellow figure. 


Setup the Data Models

Lets create out Book table using following SQL.


CREATE TABLE `sample`.`BOOK` ( `ID` INT NOT NULL AUTO_INCREMENT , `ISBMNUMBER` VARCHAR(250) NOT NULL , `NAME` VARCHAR(500) NOT NULL , `DESCRIPTION` VARCHAR(1024) NULL , `AUTHER` VARCHAR(250) NULL , PRIMARY KEY (`ID`)) ENGINE = MyISAM;

Then lets create model class for this Book. Right click on project and add new class with name as "Book" and package as "com.nirmal.springbootrest.model".


package com.nirmal.springbootrest.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Model class for map the BOOK table using Hibernate
 * @author Nirmal Balasooriya
 *
 */
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Column(name="ISBMNUMBER")
private String isbmNumber;
private String name;
private String description;
private String auther;

/**
* This is auto generated id from database
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the isbmNumber
*/
public String getIsbmNumber() {
return isbmNumber;
}
/**
* @param isbmNumber the isbmNumber to set
*/
public void setIsbmNumber(String isbmNumber) {
this.isbmNumber = isbmNumber;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @param description the description to set
*/
public void setDescription(String description) {
this.description = description;
}
/**
* @return the auther
*/
public String getAuther() {
return auther;
}
/**
* @param auther the auther to set
*/
public void setAuther(String auther) {
this.auther = auther;
}
/**
* @param isbmNumber
* @param name
*/
public Book(String isbmNumber, String name) {
super();
this.isbmNumber = isbmNumber;
this.name = name;
}
/**
* @param isbmNumber
* @param name
* @param description
* @param auther
*/
public Book(String isbmNumber, String name, String description, String auther) {
super();
this.isbmNumber = isbmNumber;
this.name = name;
this.description = description;
this.auther = auther;
}
/**
*/
public Book() {
super();
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((isbmNumber == null) ? 0 : isbmNumber.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Book other = (Book) obj;
if (isbmNumber == null) {
if (other.isbmNumber != null)
return false;
} else if (!isbmNumber.equals(other.isbmNumber))
return false;
return true;
}
}


Note that I have added additional two methods in to model class those and hashCode and equels methods. Since im going to keep isbmNumber as unique key in the API I will used it to do the comparer the Book objects in the system.


Creating  Spring Data dto

Let create interface for bookDto with following content. In my example table primary key is not the field I use to identify the Book on the system. So because of that I had to add additional method to query database from isbmNumber.I have provide interface name as "IBookDto' and package as "com.nirmal.springbootrest.dto".

package com.nirmal.springbootrest.dto;

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.nirmal.springbootrest.model.Book;

/**
 * Book DTO
 * 
 * @author Nirmal Balasooriya
 *
 */
@Repository
public interface IBookDto extends CrudRepository<Book,Integer> {
public Book getBookByIsbmNumber(String isbmNumber);

@Transactional
@Modifying
@Query(value="delete from Book u where u.isbmNumber = ?1")
    void deleteByIsbmNumber(@Param("isbmNumber") String isbmNumber);
}


Creating  Book service

Let create interface for Book Service by right click on project and New - > Interface and provide interface name as "IBookService' and package as "om.nirmal.springbootrest.service".


package com.nirmal.springbootrest.service;

import org.springframework.stereotype.Component;
import com.nirmal.springbootrest.model.Book;

/**
 * BookService interfacce with abstract methods about Book Service
 * 
 * @author Nirmal Balasooriya
 *
 */
@Component
public interface IBookService {
/**
* to save or update the book on database

* @param book
* @return bookIsbm
*/
public String saveOrUpdateBook(Book book);

/**
* to retrive the book from database

* @param String String
* @return book
*/
public Book getBook(String isbm);


/**
* Delete Book on database
* @param isbm
*/
public void deleteBook(String isbm);
}


Then lets create BookService actual implementation. Lets add new class with name as "BookServiceImpl" and package as "com.nirmal.springbootrest.service".

package com.nirmal.springbootrest.service;

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

import com.nirmal.springbootrest.dto.IBookDto;
import com.nirmal.springbootrest.model.Book;

/**
 * Actual implementation of IBookService
 * 
 * @author Nirmal Balasooriya
 *
 */
@Service
public class BookServiceImpl implements IBookService {

@Autowired
IBookDto bookDto;

/*
* (non-Javadoc)

* @see
* com.nirmal.springbootrest.service.IBookService#saveOrUpdateBook(com.nirmal.
* springbootrest.model.Book)
*/
public String saveOrUpdateBook(Book book) {

Book bookindb=bookDto.getBookByIsbmNumber(book.getIsbmNumber());
if(bookindb!=null) {
book.setId(bookindb.getId());
}
bookindb = bookDto.save(book);

if (bookindb != null)
return bookindb.getIsbmNumber();
return null;
}

/*
* (non-Javadoc)

* @see com.nirmal.springbootrest.service.IBookService#getBook(String)
*/
public Book getBook(String isbm) {
Book saveBook = bookDto.getBookByIsbmNumber(isbm);
return saveBook;
}

@Override
public void deleteBook(String isbm) {
bookDto.deleteByIsbmNumber(isbm);
}

}




Creating  REST controller for the API

First of all lets create common Response object for our REST API. I will name the class as "Response" inside "com.nirmal.springbootres.controller" package. As you can see I have use generic in order to return different objects on different API calls. 

package com.nirmal.springbootrest.controller;

/**
 * 
 * @author Nirmal Balasooriya
 *
 * @param <T>
 */
public class Response<T> {
private String code;
private String desc;
private T t;
/**
* @return the code
*/
public String getCode() {
return code;
}
/**
* @param code the code to set
*/
public void setCode(String code) {
this.code = code;
}
/**
* @return the desc
*/
public String getDesc() {
return desc;
}
/**
* @param desc the desc to set
*/
public void setDesc(String desc) {
this.desc = desc;
}
/**
* @return the t
*/
public T getT() {
return t;
}
/**
* @param t the t to set
*/
public void setT(T t) {
this.t = t;
}
/**
* @param code
* @param desc
* @param t
*/
public Response(String code, String desc, T t) {
super();
this.code = code;
this.desc = desc;
this.t = t;
}
}


Then lets add the REST controller to our application. I will set controller class name as "BookStoreController" and package name as "com.nirmal.springbootres.controller".

package com.nirmal.springbootrest.controller;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.ResponseBody;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nirmal.springbootrest.model.Book;
import com.nirmal.springbootrest.service.IBookService;

/**
 * Represent the REST controller for the application
 * 
 * @author Nirmal Balasooriya
 *
 */

@Controller
@Component
public class BookStoreController {

Logger logger = LoggerFactory.getLogger(BookStoreController.class);

@Autowired
IBookService iBookService;

@GetMapping("/findBook/{isbm}")
@ResponseBody
public Response<Book> findBook(@PathVariable("isbm")String isbm) {
logger.info("Book find isbm : " + isbm);
Book book = iBookService.getBook(isbm);
Response<Book> response = new Response<Book>("0", "Book can not be found", null);
if (book != null)
response = new Response<Book>("1", "Book found", book);
return response;
}

@RequestMapping(method = RequestMethod.POST, value = "/saveOrUpdate")
@ResponseBody
public Response<String> saveBookInJSON(@RequestBody Book book) {
logger.info("Book saved request" + book);
Response<String> response;
if ((book.getIsbmNumber() == null) || (book.getIsbmNumber() != null && book.getIsbmNumber().trim().isEmpty())) {
logger.info("Book saved fail ISBM_IS_MANDOTORY");
response = new Response<String>("0", "Book save faild ISBM mandotory", null);
} else if ((book.getName() == null) || (book.getName() != null && book.getName().trim().isEmpty())) {
logger.info("Book saved failed BOOK_NAME_IS_MANDOTORY");
response = new Response<String>("0", "Book save failed name mandotory", null);
} else {
try {
String savedbook = iBookService.saveOrUpdateBook(book);
response = new Response<String>("1", "Book save successful", savedbook);
logger.info("Book saved sucess" + savedbook);
} catch (Exception e) {
logger.error("Book saved failed" + e.getMessage());
response = new Response<String>("1", "Book save successful", null);
}
}
return response;
}

@RequestMapping(method = RequestMethod.POST, value = "/deleteBook")
@ResponseBody
public Response<String> deleteBookInJSON(@RequestBody String isbm) throws IOException {
logger.info("Book selete request" + isbm);
ObjectMapper mapper = new ObjectMapper();
    JsonNode actualObj = mapper.readTree(isbm);
Response<String> response;
isbm = actualObj.get("isbm").asText();
if ((isbm == null) || (isbm != null && isbm.trim().isEmpty())) {
logger.info("Book saved fail ISBM_IS_MANDOTORY");
response = new Response<String>("0", "Book delete faild ISBM mandotory", null);
} else {
try {
iBookService.deleteBook(isbm);
response = new Response<String>("1", "Book delete successful", isbm);
logger.info("Book delete sucess" + isbm);
} catch (Exception e) {
e.printStackTrace();
logger.error("Book delete failed" + e.getMessage());
response = new Response<String>("0", "Book delete fail", null);
}
}
return response;
}
}


After define the controller now we are ready with our REST API. Lets perform maven install and go to the target folder then lets run the jar file using bellow command.

java -jar RestApiSpringBoot-0.0.1-SNAPSHOT.jar

Then open new command line interface and lets run following curl command to test our API.


curl -X GET http://localhost:8080/findBook/9999

Then you should get following output as result

{"code":"0","desc":"Book can not be found","t":null}

Then lets add new Book in to API


curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{ \"isbmNumber\":\"9999\", \"name\":\"How to develop API\", \"description\":\"sample book\", \"auther\":\"Nirmal Balasooriya \" }" http://localhost:8080/saveOrUpdate
  
For this one following output will return

{"code":"1","desc":"Book save successful","t":"9999"}

Then if we perform previous curl command again now you will be get following output from the API.

{"code":"1","desc":"Book found","t":{"id":8,"isbmNumber":"9999","name":"How to develop API","description":"sample book","auther":"Nirmal Balasooriya "}}

Then lets try on delete operation 

curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{ \"isbm\":\"9999\" }" http://localhost:8080/deleteBook

Then you can see output as follows
{"code":"1","desc":"Book delete successful","t":"9999"}

You can access the source code on following GITHub location.


No comments:

Post a Comment