Friday, December 21, 2018

Spring Boot 2.1 – Building Secured RESTful API using JPA - CRUD







Spring Boot 2.1 Building Secured Rest API using JPA -CRUD

You can download the final project from below GITHUB Repository link


Spring Boot is an awesome module from Spring Framework. Once you are used to it, then working with Spring is a breeze because it takes care of all the spring container specific configurations and allows us to focus more on our application.
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration. 

Features
·         Create stand-alone Spring applications
·         Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
·         Provide opinionated 'starter' dependencies to simplify your build configuration
·         Automatically configure Spring and 3rd party libraries whenever possible
·         Provide production-ready features such as metrics, health checks and externalized configuration
·         Absolutely no code generation and no requirement for XML configuration

REST web services are very popular these days, So Let’s get our hand dirty by creating a RESTful JPA web service using Spring Boot .

Spring Boot REST Dependencies

Since web services run on the server, apart from Spring Boot we need to add Spring MVC to our project. I am using Eclipse to create my basic spring boot project, but you can also use Spring Initializr.
When we use Eclipse for creating Spring project, it actually uses Spring Initializr and configures it. Let’s get started.

Spring Boot REST Project


1>     Create new Spring Starter Project as shown in below image in STS. Or if you are using Eclipse then it should have Spring support for this, all the latest Eclipse release has built-in Spring support. 

 2>   In the next popup screen, provide project basic details. I am using Maven,     you can use Gradle too.
Name: SpringBoot2_Secured_JPA_Rest006
Packaging  -> War
Package -> com.smita.springboot
Description ->Demo project for Spring Boot2 Secured JPA Rest API
Hit on Next
3  3>   Next screen asks to add dependencies apart from Spring Boot. Add “Web” dependencies and click next. Select the project dependencies
a.      Web
b.      Security
c.       JPA
d.      DevTools
e.       Oracle (not available, therefore later we will add it explicitly later in pom.xml)


4>   We could have Hit on Finish in the earlier screen, but clicking on next provides us the Spring Initializr URL that we can use to create this project easily through the command line.
5>   Below image shows the contents of the auto-generated project Structure, Most important of these is SpringBootRestApplication class that is configured as Spring Boot application and contains java main method. When we run this class, it automatically runs our project as Spring Boot Web project.

6>   pom.xml -> lets Add Oracle database Dependencies and repository and after that your Final pom.xml will look like below:
<?xml version="1.0" encoding="UTF-8"?>
<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>
     <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.1.1.RELEASE</version>
              <relativePath /> <!-- lookup parent from repository -->
     </parent>
     <groupId>com.smita.springboot</groupId>
     <artifactId>SpringBoot2_Secured_JPA_Rest006</artifactId>
     <version>0.0.1-SNAPSHOT</version>
     <packaging>war</packaging>
     <name>SpringBoot2_Secured_JPA_Rest006</name>
     <description>Demo project for Spring Boot2 Secured JPA Rest API</description>

     <properties>
              <java.version>1.8</java.version>
     </properties>

     <dependencies>
              <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-data-jpa</artifactId>
              </dependency>
              <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-security</artifactId>
              </dependency>
              <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-web</artifactId>
              </dependency>

              <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-devtools</artifactId>
                        <scope>runtime</scope>
              </dependency>
              <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-tomcat</artifactId>
                        <scope>provided</scope>
              </dependency>
              <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
              </dependency>
              <dependency>
                        <groupId>org.springframework.security</groupId>
                        <artifactId>spring-security-test</artifactId>
                        <scope>test</scope>
              </dependency>
              <dependency>
                        <groupId>com.oracle</groupId>
                        <artifactId>ojdbc6</artifactId>
                        <version>11.2.0.3</version>
              </dependency>
     </dependencies>
     <!-- Repository for ORACLE ojdbc6. -->
     <repositories>

              <repository>
                        <id>codelds</id>
                        <url>https://code.lds.org/nexus/content/groups/main-repo</url>
              </repository>
     </repositories>


     <build>
              <plugins>
                        <plugin>
                                  <groupId>org.springframework.boot</groupId>
                                  <artifactId>spring-boot-maven-plugin</artifactId>
                        </plugin>
              </plugins>
     </build>

</project>
7>   Now we will create 2 sql files in src/main/resources folder.
a.       emp-ddl.sql (Note: delete the drop query if you want to drop the existing emp_rest Table) 
/*DROP TABLE emp_rest;*/

CREATE Table emp_rest(

employee_id numeric(6) primary key,
   
name varchar(30),

salary numeric(6,2));
b.     emp.dml.sql
INSERT into emp_rest VALUES (1,'Smita',9999.00);
INSERT into emp_rest VALUES (2,'Rita',8888.00);
INSERT into emp_rest  VALUES (3,'Raj',7777.00);
8>     Lets Modify the application.properties file in -> src/main/resources folder.
#FOR CHANGING EMBEDED TOMCAT SERVER PORT NUMBER
server.port=8082
#DATASOURCE - ORACLE
spring.datasource.schema=classpath:/emp-ddl.sql
spring.datasource.data=classpath:/emp-dml.sql
spring.datasource.initialization-mode=always
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/orahome
spring.datasource.username=system
spring.datasource.password=system
#JPA
spring.jpa.generate-ddl=false
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
spring.jpa.show-sql=true
#SECURITY
spring.mvc.dispatch-options-request=true
spring.security.user.name=smita
spring.security.user.password=smita@123
spring.security.enable-csrf=false # Enable Cross Site Request Forgery support.
#IF YOU DONT WANT DEAFULT ERROR THEN MAKE IT FALSE
server.error.whitelabel.enabled=true


9>   Now let’s do the maven build
Right click on project -> run as -> Maven build …


10>                      Now enter the Goals -> clean package -> Run

11>                      Following output you will receive in the console after running maven build -> clean  package
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.1.RELEASE)
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-war-plugin:3.2.2:war (default-war) @ SpringBoot2_Secured_JPA_Rest006 ---
[INFO] Packaging webapp
[INFO] Assembling webapp [SpringBoot2_Secured_JPA_Rest006] in [E:\Spring boot\WS\SpringBoot2_Secured_JPA_Rest006\target\SpringBoot2_Secured_JPA_Rest006-0.0.1-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [E:\Spring boot\WS\SpringBoot2_Secured_JPA_Rest006\src\main\webapp]
[INFO] Webapp assembled in [1267 msecs]
[INFO] Building war: E:\Spring boot\WS\SpringBoot2_Secured_JPA_Rest006\target\SpringBoot2_Secured_JPA_Rest006-0.0.1-SNAPSHOT.war
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.1.RELEASE:repackage (repackage) @ SpringBoot2_Secured_JPA_Rest006 ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 37.004 s
[INFO] Finished at: 2018-12-21T16:01:04+05:30
[INFO] ------------------------------------------------------------------------
12>                       Now right click on project -> Run As -> Spring Boot App

13>                    In Console you see that
a.      Embedded tomcat server will start.
b.      Implicitly DataSource will be created by HikariPool CP (Connection Pool)
c.       JPA Entity Manager initialized
d.      Spring Security filter initialized
e.    Magic of SpringBoot project started in just few seconds 😊

14> Now open Browser and type -> localhost:8082
You will find that SpringBootSecurity login page will Appear


15>          Enter the username and password which we had set in the application.properties file
Username: smita
Password: smita@123
16>          Now you will see the SpringBoot default error page as we have not created any Controller.

17>       Now that our base Spring Boot REST application is ready, it’s time to add some endpoints and business logic to it.
We will use a java bean class for sending REST web service response. Let’s Create a Entity, Repository, Service and RestController
18>Create Employee class in the package com.smita.springboot.entities

package com.smita.springboot.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
/** @author Smita **/
@Entity // tell JPA that this class object need to be persisted
@Table(name="emp_rest") // tell JPA that this class object need to be mapped with this table
public class Employee {
   @Id //Primary key
   //@GeneratedValue(strategy=GenerationType.TABLE)
   @SequenceGenerator(name="EMP_GEN" ,sequenceName="emp_master_seq",allocationSize=1)
   @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="EMP_GEN")
   @Column(name="employee_id")
   private Long employeeId;    
   private String name;
   private Double salary;
//no-arg constr      
   public Employee() {
          // TODO Auto-generated constructor stub
   }
   //overloaded constr
   public Employee(Long employeeId, String name, Double salary) {
          super();
          this.employeeId = employeeId;
          this.name = name;
          this.salary = salary;
   }     
   public Employee(String name, Double salary) {
                 super();
                 this.name = name;
                 this.salary = salary;
   }
//getters and setters
   public Long getEmployeeId() {
          return employeeId;
   }
   public void setEmployeeId(Long employeeId) {
          this.employeeId = employeeId;
   }
   public String getName() {
          return name;
   }
   public void setName(String name) {
          this.name = name;
   }
   public Double getSalary() {
          return salary;
   }
   public void setSalary(Double salary) {
          this.salary = salary;
   }
   //toString()
   @Override
   public String toString() {
return "Employee [employeeId=" + employeeId + ", name=" + name + ", salary=" + salary + "]";
   }     
}

19>     Create EmployeeException class in the package com.smita.springboot.exception

package com.smita.springboot.exception;
/** @author Smita **/
public class EmployeeException extends Exception {
   private static final long serialVersionUID = -1648287564068549359L;

   public EmployeeException() {
   super();
   }
   public EmployeeException(String message) {
   super(message);
   }
}
20>     Create IEmployeeDao interface in the package com.smita.springboot.dao
package com.smita.springboot.dao;
import java.util.List;
import com.smita.springboot.entities.Employee;
import com.smita.springboot.exception.EmployeeException;
/** @author Smita **/
public interface IEmployeeDao {
   public Long addEmployee(Employee employee)throws EmployeeException;//C-create
   public List<Employee> getEmployeeList()throws EmployeeException;//R All Employee -retrieve
   public boolean updateEmployee(Employee employee)throws EmployeeException;//U-update
   public boolean deleteEmployeeById(Long empId)throws EmployeeException;//D-delete
   public Employee getEmployeeById(Long empId)throws EmployeeException;//S-search
}

21>     Create EmployeeDao class in the package com.smita.springboot.dao
package com.smita.springboot.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import com.smita.springboot.entities.Employee;
import com.smita.springboot.exception.EmployeeException;
/** @author Smita **/
//prep-work 1- @Repository EmployeeDao
@Repository
public class EmployeeDao implements IEmployeeDao {
     //prep-work 2 -> @PersistenceContext EnttityManager Object
     @PersistenceContext
     private EntityManager entityManager;
     @Override
     public Long addEmployee(Employee employee) throws EmployeeException {
              entityManager.persist(employee);
              return employee.getEmployeeId();
     }

     @SuppressWarnings("unchecked")
     @Override
     public List<Employee> getEmployeeList() throws EmployeeException {
              Query query = entityManager.createQuery("from Employee");
              return query.getResultList();
     }

     @Override
     public Employee getEmployeeById(Long empId) throws EmployeeException {
              // TODO Auto-generated method stub
              return entityManager.find(Employee.class, empId);
     }

     @Override
     public boolean updateEmployee(Employee employee) throws EmployeeException {
              // TODO Auto-generated method stub
              return entityManager.merge(employee)!=null;
     }

     @Override
     public boolean deleteEmployeeById(Long empId) throws EmployeeException {
              Employee employee =getEmployeeById(empId);
              if(employee!=null)
                        return false;
              else {
                        entityManager.remove(employee);
                        return true;
              }
     }
}

22>     Create IEmployeeService interface in the package com.smita.springboot.service
package com.smita.springboot.service;
import java.util.List;
import com.smita.springboot.entities.Employee;
import com.smita.springboot.exception.EmployeeException;
/** @author Smita **/
public interface IEmployeeService {
    //CRUDS Operation
    public Long addEmployee(Employee employee)throws EmployeeException;//C-create
    public List<Employee> getEmployeeList()throws EmployeeException;//R -retrieve
    public boolean updateEmployee(Employee employee)throws EmployeeException;//U-update
    public boolean deleteEmployeeById(Long empId)throws EmployeeException;//D-delete
    public Employee getEmployeeById(Long empId)throws EmployeeException;//S-search
   }

23>       Create EmployeeService class in the package com.smita.springboot.service
package com.smita.springboot.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.smita.springboot.dao.IEmployeeDao;
import com.smita.springboot.entities.Employee;
import com.smita.springboot.exception.EmployeeException;
/** @author Smita **/
//prepwork 1- @Service EmployeeService
@Service
public class EmployeeService implements IEmployeeService {
//prepwork 2- @Autowired IEmployeeDao
   @Autowired
   private IEmployeeDao employeeDao;
  
   @Override
   @Transactional
   // prepwork 3- @Transactional all method (@Transactional we have to import from
   // springFramework)
   public Long addEmployee(Employee employee) throws EmployeeException {
          // prepwork 4- call dao layer method and return to Client
          return employeeDao.addEmployee(employee);// auto generated emp id will be returned
   }

   @Override
   public List<Employee> getEmployeeList() throws EmployeeException {
          // call Dao layer method and return to Service layer
          return employeeDao.getEmployeeList();
   }

   @Override
   public Employee getEmployeeById(Long empId) throws EmployeeException {
          // call Dao layer method and return to Service layer
          return employeeDao.getEmployeeById(empId);
   }

   @Override
   public boolean updateEmployee(Employee employee) throws EmployeeException {
          return employeeDao.updateEmployee(employee);
   }

   @Override
   public boolean deleteEmployeeById(Long empId) throws EmployeeException {
          return employeeDao.deleteEmployeeById(empId);
   }

}

Spring REST Endpoints Configuration

24>               We will create a @RestController and add some methods and annotate them with @RequestMapping. Below is our REST Controller class that exposes few GET and POST endpoints. Create EmployeeController class in the package com.smita.springboot.controller
package com.smita.springboot.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.smita.springboot.entities.Employee;
import com.smita.springboot.exception.EmployeeException;
import com.smita.springboot.service.IEmployeeService;
/** @author Smita **/
/*This @CrossOrigin annotation enables cross-origin requests only for controller or the specific method. By default, its allows all origins, all headers, the HTTP methods specified in the @RequestMapping annotation and a maxAge of 30 minutes is used. You can customize this behavior by specifying the value of one of the annotation attributes: origins, methods, allowedHeaders, exposedHeaders, allowCredentials or maxAge. In this example, we only allow http://localhost:8082 to send cross-origin requests.*/
//prep-work 1> @CrossOrigin
@CrossOrigin(origins = "*", allowedHeaders = "*")
//prep-work 2> @RestController
@RestController
//prep-work 3> @RequestMapping
@RequestMapping("/api/employees")
public class EmployeeController {
   //prep-work 4> @Autowired IEmployeeService
   @Autowired private IEmployeeService employeeService;
  
   @GetMapping(value="",produces="application/json")
   public List<Employee> getAll() throws EmployeeException{
                 return employeeService.getEmployeeList();
   }
   @GetMapping("/{employeeId}")
    public ResponseEntity<Employee> getEmployeeById(@PathVariable(value = "employeeId") Long employeeId) throws EmployeeException{
          System.out.println("EmployeeController -> find : "+employeeId);
          Employee employee =employeeService.getEmployeeById(employeeId);
          return ResponseEntity.ok().body(employee);
   }
   @PostMapping("/save")
    public HttpStatus  saveEmployee(@RequestBody Employee employee) throws EmployeeException {
          return employeeService.addEmployee(employee) != null? HttpStatus.CREATED : HttpStatus.BAD_REQUEST;
    }
   @PutMapping("/{employeeId}")
    public HttpStatus updateEmployee(@PathVariable(value = "employeeId") Long employeeId,
         @Valid @RequestBody Employee employeeDetails) throws EmployeeException  {
        Employee employee = employeeService.getEmployeeById(employeeId);
        if(employee==null)
           return HttpStatus.BAD_REQUEST;
        else {
        employee.setName(employeeDetails.getName());
        employee.setSalary(employeeDetails.getSalary());
        return employeeService.updateEmployee(employee)? HttpStatus.ACCEPTED : HttpStatus.BAD_REQUEST;
        }        
    }
   @DeleteMapping("/{employeeId}")
    public Map<String, Boolean> deleteEmployee(@PathVariable(value = "employeeId") Long employeeId) throws EmployeeException{
           Employee employee = employeeService.getEmployeeById(employeeId);
           Map<String, Boolean> response = new HashMap<>();
           if(employee!=null) {
                 employeeService.deleteEmployeeById(employeeId);
                
               response.put("deleted", Boolean.TRUE);
           }       
        return response;
    }
}
·         When we annotate the controller class with @RestController, Spring Boot scans it to find the REST endpoints to configure.
·         @RequestMapping annotation on methods tells Spring to use this method as handler for any client requests. This is very important spring annotation, we can configure additional features such as HTTP methods supported, Content-Type of request, response content type etc. By default, GET method is supported and JSON response is returned.
That’s it, our Spring Boot RESTful web service is ready to run and test.

1>           Auto-Generated SpringBoot2SecuredJpaRest006Application class in the package com.smita.springboot By Spring Boot Starter Project

package com.smita.springboot;
/** @author Smita **/
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBoot2SecuredJpaRest006Application {
       public static void main(String[] args) {
              SpringApplication.run(SpringBoot2SecuredJpaRest006Application.class, args);
       }
}

2>            Auto-Generated ServletInitializer class in the package com.smita.springboot By Spring Boot Starter Project.
package com.smita.springboot;
/** @author Smita **/
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
   @Override
   protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
       return application.sources(SpringBoot2SecuredJpaRest006Application.class);
       //return application.sources(JavaApplication.class);//for stand-alone java application
   }
}

3>                Run As Maven-> Build ->Then finally,Now its time to Run your project as Spring Boot App.


4>   Now open Browser and type -> localhost:8082/employees
You will find that SpringBootSecurity login page will Appear

5>    Enter the     Username: smita 
Password: smita@123
               -> Login -> You can see the List of Employees


I am using Postman Chrome app for testing POST requests.

Spring Boot REST JSON Request Response

JSON is the standard messaging format for REST web services, our method accept JSON request.

Summary

In this post, we learned how to use Spring Boot to create a RESTful JPA web service. Spring Boot did all the configurations and we only had to focus on our business logic. You can download the final project from below GITHUB Repository link.