Spring Boot + Angular CRUD Tutorial - Part #1 - Build Spring Boot CRUD Rest APIs


This is Part 1 of Spring Boot + Angular 8 CRUD Example Tutorial, In this part 1, we will develop CRUD RESTFul APIs using Spring boot and MySQL database.

Use the below links to visit different parts of this tutorial:
  1. Spring Boot + Angular 8 CRUD Example Tutorial - Main Tutorial
  2. Spring Boot + Angular 8 CRUD, Part 1 - Develop Spring Boot CRUD Rest APIs
  3. Spring Boot + Angular 8 CRUD, Part 2 - Create Angular 8 App
  4. Spring Boot + Angular 8 CRUD, Part 3 - Develop Angular 8 CRUD Operations
  5. Spring Boot + Angular 8 CRUD, Part 4 - Angular 8 CRUD App Configuration
  6. Spring Boot + Angular 8 CRUD, Part 5 - Running Angular 8 CRUD App

High-level architecture of Spring boot project

Spring Boot CRUD Rest APIs Development

Here are the five REST endpoints that we are going to develop in this part 1:

1. Creating and Importing a Project

There are many ways to create a Spring Boot application. The simplest way is to use Spring Initializrat http://start.spring.io/, which is an online Spring Boot application generator.
Go to http://start.spring.io website to create a Spring Boot project:
Look at the above diagram, we have specified the following details:
  • Generate: Maven Project
  • Java Version: 1.8 (Default)
  • Spring Boot Version: keep default selected
  • Group: net.guides.springboot2
  • Artifact: springboot2-jpa-crud-example
  • Name: springboot2-jpa-crud-example
  • Description: Rest API for a Simple Employee Management Application
  • Package Name : net.guides.springboot2.springboot2jpacrudexample
  • Packaging: jar (This is the default value)
  • Dependencies: Web, JPA, MySQL, DevTools
Once, all the details are entered, click on Generate Project button will generate a spring boot project and downloads it. Next, Unzip the downloaded zip file and import it into your favorite IDE.

2. Packaging Structure

Following is the packing structure of our Employee Management System -

3. The pom.xml File

Here is the complete pom.xml file for your reference:
<?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>

    <groupId>net.guides.springboot2</groupId>
    <artifactId>springboot2-jpa-crud-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springboot2-jpa-crud-example</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <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-web</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-devtools</artifactId>
           <scope>runtime</scope>
       </dependency>
       <dependency>
           <groupId>com.h2database</groupId>
           <artifactId>h2</artifactId>
           <scope>runtime</scope>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
      </dependency>
    </dependencies>

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

The spring-boot-starter-web Starter:

We use spring-boot-starter-web dependency to develop RESTful web services.
This dependency provides tomcat server as the default embedded container:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

The spring-boot-starter-data-jpa Starter:

We use spring-boot-starter-data-jpa dependency to talk with the database.
This dependency internally uses Hibernate as the default JPA provider:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

The spring-boot-starter-test Starter:

For testing, we usually use the following set of libraries: Spring Test, JUnit, Hamcrest, and Mockito. We can include all of these libraries manually, but the below spring-boot-starter-test dependency can be used to automatically include these libraries in the following way:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

4. Configuring Database (H2 or MySQL)

H2 Database Configuration

In this project, we will use the H2 database to quickly set up and run a spring boot project without installing databases.

Note that we have added the below dependency in the pom.xml file:
       <dependency>
           <groupId>com.h2database</groupId>
           <artifactId>h2</artifactId>
           <scope>runtime</scope>
       </dependency>

If you use the H2 database then we no need to configure the database-related properties in the application.properties file.
If you use H2 in-memory database then you no need to create a database and tables, spring boot automatically do it for you.
In this project, we are going to use a context path for the spring boot project so let's add the below property in the application.properties file:
server.servlet.context-path=/springboot-crud-rest

MySQL Database Configuration

You can also use the MySQL database but make sure that you follow the below steps to configure the MySQL database in this spring boot project.

Step 1:  Replace the H2 database dependency with MySQL dependency:
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <scope>runtime</scope>
       </dependency>
Step 2: Configure application.properties to connect to your MySQL database. Add the following content to the application.properties file:
spring.datasource.url = jdbc:mysql://localhost:3306/users_database?useSSL=false
spring.datasource.username = root
spring.datasource.password = root


## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update

server.servlet.context-path=/springboot-crud-rest
Make sure that you will change the above database configuration such as JDBC URL, username, and password as per your environment.
 Step 3:
 You need to create a database in MySQL server with the following command:
create database users_database
Hibernate will automatically create database tables so you only need to manually create the database and configure an application.properties file.

5. Create JPA Entity - Employee.java

Let's create an Employee JPA entity with the following fields:
  • id - primary key
  • firstName - First name of an employee
  • lastName - Last name of an employee
  • email - Email address of an employee
package net.guides.springboot2.springboot2jpacrudexample.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;

@Entity
@Table(name = "employees")
public class Employee {

    private long id;
    private String firstName;
    private String lastName;
    private String emailId;
 
    public Employee() {
  
    }
 
    public Employee(String firstName, String lastName, String emailId) {
         this.firstName = firstName;
         this.lastName = lastName;
         this.emailId = emailId;
    }
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
        public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
 
    @Column(name = "first_name", nullable = false)
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
 
    @Column(name = "last_name", nullable = false)
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
 
    @Column(name = "email_address", nullable = false)
    public String getEmailId() {
        return emailId;
    }
    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", emailId=" + emailId
       + "]";
    }
 
}
All your domain models must be annotated with @Entity annotation. It is used to mark the class as a persistent Java class.
@Table annotation is used to provide the details of the table that this entity will be mapped to.
@Id annotation is used to define the primary key.
@GeneratedValue annotation is used to define the primary key generation strategy. In the above case, we have declared the primary key to be an Auto Increment field.
@Column annotation is used to define the properties of the column that will be mapped to the annotated field. You can define several properties like name, length, nullable, updateable, etc.

6. Create a Spring Data Repository - EmployeeRepository.java

Let's create an EmployeeRepository to access Employee's data from the database.
Well, Spring Data JPA has comes with a JpaRepository interface that defines methods for all the CRUD operations on the entity, and a default implementation of JpaRepository called SimpleJpaRepository.
package net.guides.springboot2.springboot2jpacrudexample.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import net.guides.springboot2.springboot2jpacrudexample.model.Employee;

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long>{

}

7. Create Spring Rest Controller - EmployeeController.java

Let's create the REST APIs for creating, retrieving, updating, and deleting an Employee:
package net.guides.springboot2.springboot2jpacrudexample.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.ResponseEntity;
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 net.guides.springboot2.springboot2jpacrudexample.exception.ResourceNotFoundException;
import net.guides.springboot2.springboot2jpacrudexample.model.Employee;
import net.guides.springboot2.springboot2jpacrudexample.repository.EmployeeRepository;

@RestController @CrossOrigin(origins = "http://localhost:4200")
@RequestMapping("/api/v1")
public class EmployeeController {
    @Autowired
    private EmployeeRepository employeeRepository;

    @GetMapping("/employees")
    public List<Employee> getAllEmployees() {
        return employeeRepository.findAll();
    }

    @GetMapping("/employees/{id}")
    public ResponseEntity<Employee> getEmployeeById(@PathVariable(value = "id") Long employeeId)
        throws ResourceNotFoundException {
        Employee employee = employeeRepository.findById(employeeId)
          .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));
        return ResponseEntity.ok().body(employee);
    }
    
    @PostMapping("/employees")
    public Employee createEmployee(@Valid @RequestBody Employee employee) {
        return employeeRepository.save(employee);
    }

    @PutMapping("/employees/{id}")
    public ResponseEntity<Employee> updateEmployee(@PathVariable(value = "id") Long employeeId,
         @Valid @RequestBody Employee employeeDetails) throws ResourceNotFoundException {
        Employee employee = employeeRepository.findById(employeeId)
        .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

        employee.setEmailId(employeeDetails.getEmailId());
        employee.setLastName(employeeDetails.getLastName());
        employee.setFirstName(employeeDetails.getFirstName());
        final Employee updatedEmployee = employeeRepository.save(employee);
        return ResponseEntity.ok(updatedEmployee);
    }

    @DeleteMapping("/employees/{id}")
    public Map<String, Boolean> deleteEmployee(@PathVariable(value = "id") Long employeeId)
         throws ResourceNotFoundException {
        Employee employee = employeeRepository.findById(employeeId)
       .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

        employeeRepository.delete(employee);
        Map<String, Boolean> response = new HashMap<>();
        response.put("deleted", Boolean.TRUE);
        return response;
    }
}

Enable CORS on the Server

To enable CORS on the server, add a @CrossOrigin annotation to the EmployeeController:
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api/v1")
public class EmployeeController {
 // ....
}

8. Exception(Error) Handling for RESTful Services

Spring Boot provides a good default implementation for exception handling for RESTful Services. Let’s quickly look at the default Exception Handling features provided by Spring Boot.

Resource Not Present

Here's what happens when you fire a request to not resource found: http://localhost:8080/some-dummy-url
{
  "timestamp": 1512713804164,
  "status": 404,
  "error": "Not Found",
  "message": "No message available",
  "path": "/some-dummy-url"
}
That's a cool error response. It contains all the details that are typically needed.

What happens when we throw an Exception?

Let’s see what Spring Boot does when an exception is thrown from a Resource. we can specify the Response Status for a specific exception along with the definition of the Exception with ‘@ResponseStatus’ annotation.
Let's create a ResourceNotFoundException.java class.
package com.companyname.springbootcrudrest.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends Exception{

    private static final long serialVersionUID = 1L;

    public ResourceNotFoundException(String message){
        super(message);
    }
}

Customizing Error Response Structure

Default error response provided by Spring Boot contains all the details that are typically needed.
However, you might want to create a framework independent response structure for your organization. In that case, you can define a specific error response structure.
Let’s define a simple error response bean.
package com.companyname.springbootcrudrest.exception;

import java.util.Date;

public class ErrorDetails {
    private Date timestamp;
    private String message;
    private String details;

    public ErrorDetails(Date timestamp, String message, String details) {
        super();
        this.timestamp = timestamp;
        this.message = message;
        this.details = details;
    }

    public Date getTimestamp() {
        return timestamp;
    }

    public String getMessage() {
         return message;
    }

    public String getDetails() {
         return details;
    }
}
To use ErrorDetails to return the error response, let’s create a GlobalExceptionHandler class annotated with @ControllerAdvice annotation. This class handles exception-specific and global exceptions in a single place.
package com.companyname.springbootcrudrest.exception;

import java.util.Date;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
         ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
         return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> globleExcpetionHandler(Exception ex, WebRequest request) {
        ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
        return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

9. Running Application

This spring boot application has an entry point Java class called Application.java with the public static void main(String[] args) method, which you can run to start the application.
import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
The main() method uses Spring Boot’s SpringApplication.run() method to launch an application.
Or you can start the spring boot application via the command line using the below command:
$ mvn spring-boot:run

Testing REST APIs

Use below Rest endpoints to test CRUD Rest APIs and in Angular application.

Get All Employees:

HTTP Method: GET
http://localhost:8080/springboot-crud-rest/api/v1/employees

Get Employee By Id:

HTTP Method GET
http://localhost:8080/springboot-crud-rest/api/v1/employees/{employeeId}

Create Employee:

HTTP Method - POST
http://localhost:8080/springboot-crud-rest/api/v1/employees

Update Employee

HTTP Method - PUT
http://localhost:8080/springboot-crud-rest/api/v1/employees/{employeeId}

Delete Employee By Id:

HTTP Method - DELETE
http://localhost:8080/springboot-crud-rest/api/v1/employees/{employeeId}

This completes the development of Spring boot CRUD Rest APIs.

What's next? 

In this part 1, we have developed CRUD (Create, Read, Update, and Delete) REST APIs using Spring Boot.

In the next Part 2, we will implement the following steps:
  • Install the latest version of Angular CLI
  • Create Angular 8 client application using Angular CLI
  • Identify Components, Services, and Modules
  • Create Service & Components using Angular CLI
  • Integrate JQuery and Bootstrap with Angular

Comments

  1. Hi this is a very good tutorial, I was able to learn the fundamentals of Spring Boot and Angular and I was able to run the project and also created my own. Thank you very much

    I have a question tho, I'd like to use the API into a simple html file with Jquery ajax. But whenever I try POST or GET I always have this error in the console:

    Access to XMLHttpRequest at 'http://localhost:8080/mynotes/api/v1/mynotes?' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

    May I ask how can I fix this issue, is it in my ajax request? or with the spring boot app? Thank you.

    ReplyDelete
  2. Hi i have doubt like
    do we need to create database and table in MySql or will it created automatically during running application ?

    ReplyDelete
    Replies
    1. You only need to manually create the database and Hibernate will automatically create tables in that database. I updated this tutorial with explanation so you can have a look.

      Delete
  3. I needed to modify the spring.datasource.url and it works perfectly right now.

    spring.datasource.url = jdbc:mysql://localhost/users_database?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

    ReplyDelete
  4. In IntelliJ, the EmployeeController class just fails to do anything with the imports of javax.validation.Valid and the ResourceNotFoundException. There is just no "there" there, and it does not compile of course. I tried to add fitting Maven dependencies, but I just cannot make it work. Any idea what the reason might be?

    ReplyDelete
  5. Please update your UPDATE Rest Api to PUT HTTP methos instead of POST

    ReplyDelete

Post a Comment

Leave Comment