How to build a Spring Boot REST API From Scratch

Google Spring Initializr

plot
plot

Enter the following values / Select the choices chonse below

- 1. Maven as Built Tool
- 2. JDK 17
- 3. Spring 3.2.5 or later
- 4. group name as com.rollingstone
- 5. artifact name :
- 6. package name : 

Click Add Dependencies button on the right

plot
plot
- 1. Search for Web and Add it
- 2. Click Add Dependencies again, Search for JPA and Add it
- 3. Click Add Dependencies again, Search for MySQL and Add it
- 4. Click Add Dependencies again, Search for H2 and Add it
= 5. Click Add Dependencies again, Search for Actuator and Add it

See below

plot
plot

Create Java Packages

Right-click on the com.rollingstone package and choose

plot
plot

Create model package

plot
plot

Create the controller package

Create Model Class

Expense.java

plot
plot

Lets create a Model Class

Right-click on the com.rollingstone.model package and choose

Enter the following attributes / fields

plot
plot

Image 9

plot
plot

Choose Code -> Generate -> Getters and Setters

plot
plot

Choose Code -> Generate -> Constructor

plot
plot

Choose Code -> Generate -> Constructor

Choose Select None to create a Blank Constructor

plot
plot

Choose Code -> Generate -> toString

plot
plot

Full Code

package com.rollingstone.model;

import java.math.BigDecimal;

public class Expense {

    private int expenseId;

    private String expenseName;

    private BigDecimal expenseAmount;

    public Expense(int expenseId, String expenseName, BigDecimal expenseAmount) {
        this.expenseId = expenseId;
        this.expenseName = expenseName;
        this.expenseAmount = expenseAmount;
    }

    public Expense() {
    }

    public int getExpenseId() {
        return expenseId;
    }

    public void setExpenseId(int expenseId) {
        this.expenseId = expenseId;
    }

    public String getExpenseName() {
        return expenseName;
    }

    public void setExpenseName(String expenseName) {
        this.expenseName = expenseName;
    }

    public BigDecimal getExpenseAmount() {
        return expenseAmount;
    }

    public void setExpenseAmount(BigDecimal expenseAmount) {
        this.expenseAmount = expenseAmount;
    }

    @Override
    public String toString() {
        return "Expense{" +
                "expenseIdl=" + expenseId +
                ", expenseName='" + expenseName + '\'' +
                ", expenseAmount=" + expenseAmount +
                '}';
    }
}

Create a Controller Class

Right-click on the com.rollingstone.controller package

Choose New Class : Enter class name as ExpenseController and Press Enter

Process

- 1. Enter @RestController at the top line before the class name
- 2. Click on the RestController and choose Import Class
- 3. One new Import will be added by the IDE at the top of the class after package
- 4. Enter @GetMapping("/") in a blank line
- 5. Enter  @ResponseBody in the next line
- 6. Enter  public List<Expense> getExpenses(){ in the next line and press enter
- 7. Click on the GetMapping and choose Import Class
- 8. Click on the ResponseBody and choose Import Class

Enter the following code

package com.rollingstone.controller;


import com.rollingstone.model.Expense;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@RestController
public class ExpenseController {
    
    @GetMapping("/")
    @ResponseBody
    public List<Expense> getExpenses(){
        List<Expense> expenses = new ArrayList<>();
        
        Expense expense01 = new Expense(1, "Gas Expenses", new BigDecimal(43.00));
        Expense expense02 = new Expense(1, "Food Expense", new BigDecimal(93.00));
        Expense expense03 = new Expense(1, "Entertainment Expenses", new BigDecimal(33.00));

        expenses.add(expense01);
        expenses.add(expense02);
        expenses.add(expense03);
        
        return expenses;
    }
}

Open the resources folder and find application.properties file

Enter the following code server.port=3091 to change the default port from 8080

spring.application.name=spring-boot-personal-expense-api
server.port=3091

Click on the Terminal Tab below in your IntelliJ IDE

Enter

mvn clean install

Build Success

plot
plot

Now enter

java -jar target/spring-boot-personal-expense-api-0.0.1-SNAPSHOT.jar

Application is Running

plot
plot

Open the resources folder and double-click on application.properties file

plot
plot

aa

plot
plot

aa

plot
plot

aa

plot
plot

Image 19

plot
plot

Syntax / Meaning of the Application

- 1. Any Spring Boot REST API Application is a hierarchical Folder structure
- 2. All other software applications like User Interface, Python Projects are also similar
- 3. Java Source Code are kept in the src/main/java folder
- 4. Under the src/main/java folder we create our individual java package structures
- 5. Java packages are also special folders recognized by Java compiler to give a better structure for our Application
- 6. The Expense.java and the ExpenseController.java are both Java Classes but Serving different purposes
- 7. We normally create java packages to group classes that serve similar purposes
- 8. One class class can use the import statement to import classes from other packages

At this stage, we have some hardcoded Expenses that we can retreive from the Application.

However, we need to be able to Create (C), Retreive (R) , Update (U) and Delete (D) or provide CRUD Functionality from our Application.

Adding Java Persistence API (JPA) and MySQL Database to our Application.

Why Databases are important :

- 1. Imagine Writing an Essay in Microsoft Word and not Saving the Document to Disk
- 2. If there is a Power Outage or the Laptop Crashes, we will lose our Document / Work / Essay
- 3. Like Hard disks store our Word / Excel Documents, Photos, Songs and Videos for Retrival after Restart of the Application or computer, Databases do the same for Data
- 4. They can save Data (ExpensesId, ExpenseName, ExpenseAmount) and numerous others for safe storage and safe retrival later.

We will use MySQL a Relational Database. There are other Relational Database like Oracle, DB2, Microsoft SQL Server etc.

In a Relational Database, data are stored in tables.

Tables are devided into columns

Example Table

plot
plot

The table above has the following columns:

- 1. ID
- 2. Meeting_id
- 3. field_key
- 4. field_value

What is Java Persistence API (JPA)

- 1. JPA Helps us move from one Relational Database to another without changing our Code
- 2. Like we can change our iPhone Covers without changing the iPhone itself.
- 3. All Relaitonal Database maintain some standards for all applications to connect to them
- 4. We can query RDBMS using a language called Structured Query Language (SQL)
- 5. SQL Works across all the RDBMS like we can drive our Cars across all roads.
- 6. JPA sheilds our Application Code from the underlying RDBMS Product Differences
- 7. We can change a few properties in our application properties file and our RDBMS wil change
- 8. In other words, the following diagram will show us how JPA protects our application from having hardcoded knowledge or the RDBMS.
plot
plot

The diagram shows how the same application can with different application.properties file for different RDBMSs

What is a Database Driver

- 1. A RDBMS Driver is a bunch of Programs written by the Database Vendor to be able to talk to the Database
- 2. Like If we are in China, we would need an Interpreter who would understand our English and translate that into Chinese and Vice Versa.
- 3. We are writing Java and the Databases understand SQL
- 4. JPA Translates our Java commands syntax into SQL
- 5. The Resulting SQL still needs to be translated into the Database's Native language
- 6. The Database Driver does this final translation of SQL

We need dependencies or third party libraries

Like we need to buy ingredients of our favorite Recipe to prepare, one, we need dependencies to prepare an Application software.

These dependencies are available as Jar Files (Zipped archives) in the Internet.

We are using Maven to download them for us and package them with our application code

Following code shows the deps

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <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>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

H2 is a memory held database with the driver. We use H2 during Build Time.

<dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
</dependency>

JPA is dependency as well

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

How to download and install MySQL

plot
plot

Download The First One and Install By Double-Clicking the Installer

The installation will create a local MySQL Database we can use

plot
plot

How to update properties for MySQL

Before we proceed, we wqould also need a MySQL Client Application

plot
plot

Download Datagrip

plot
plot

Start Datagrip and Connect to the Localhost DB

Create a new Database with the command

create database expense_tracker

Create a new Properties File named application-mysql.properties

Put these into the new file

spring.application.name=spring-boot-personal-expense-api
server.port=3091

spring.datasource.url=jdbc:mysql://localhost:3306/expense_tracker
spring.datasource.username=root
spring.datasource.password=localroot
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.database=default
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

How to tell JPA about our Database Linked Plain Old Java Objects (POJO)

We do that using JPA Annotations

Anyting that starts with an @ is called an Annotation

- 1. @RestController
- 2. @GetMapping

Example of a Non JPA Pojo

public class Expense {

    private int expenseId;

    private String expenseName;

    private BigDecimal expenseAmount;
....

First do this

- 1. Create a New Java Package to keep the code changes separate, named entity
- 2. Copy the Expense.java to that java package

Then add the following on top of the public class Expense statement

IDE would add the imports automatically.

package com.rollingstone.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.Table;

import java.math.BigDecimal;

@Entity
@Table(name = "PERSONAL_EXPENSE_TRACKER")
public class Expense {

    private int expenseId;

    private String expenseName;

    private BigDecimal expenseAmount;

Add the two annotations above the expenseId statement

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 @Column(name = "EXPENSE_ID")
 private int expenseId;

Why

- 1. In a School Class , every Student has a unique ID
- 2. Our Driving License ID is also unique
- 3. Similarly every RDBMS also needs to have a unique ID called the Primary Key
- 4. We are telling JPA that the expenseId is our Primary Key in the table
- 5. No two rows in the same table will have the same Primary Key value
- 6. We are also telling JPA to auto generate the value for the Primary Key Column
- 7. Then we are telling JPA that the underlying DB Column would be named as EXPENSE_ID

Add @Column annotation

 @Column(name = "EXPENSE_NAME")
    private String expenseName;

    @Column(name = "EXPENSE_AMOUNT")
    private BigDecimal expenseAmount;

Full Entity with all JPA Annotations

package com.rollingstone.entity;

import jakarta.persistence.*;

import java.math.BigDecimal;

@Entity
@Table(name = "PERSONAL_EXPENSE_TRACKER")
public class Expense {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "EXPENSE_ID")
    private int expenseId;


    @Column(name = "EXPENSE_NAME")
    private String expenseName;

    @Column(name = "EXPENSE_AMOUNT")
    private BigDecimal expenseAmount;

The other code is absolutely the same with non JPA Pojo

Now we need to build the Create Method

Create a new Package called repository under com.rollingstone

Create this Java Interface in the new package

package com.rollingstone.repository;

import com.rollingstone.entity.Expense;
import org.springframework.data.repository.CrudRepository;

public interface ExpenseRepository extends CrudRepository<Expense, Long> {
}

TODO Explain how JPA Interfaces work

Create a new package called services under com.rollingstone

Create a new class called ExpenseService in the package

package com.rollingstone.services;

import com.rollingstone.entity.Expense;
import com.rollingstone.repository.ExpenseRepository;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class ExpenseService {

    ExpenseRepository expenseRepository;
    public ExpenseService(ExpenseRepository expenseRepository){
        this.expenseRepository = expenseRepository;
    }

    public Expense save(Expense expense) {
        return this.expenseRepository.save(expense);
    }

    public List<Expense> getAll() {
        List<Expense> target = new ArrayList<Expense>();

        Iterable<Expense> iterable = this.expenseRepository.findAll();

        iterable.forEach(target::add);

        return target;
    }
}

Update the Controller ExpenseController

Add the following code first

  ExpenseService expenseService;

    public ExpenseController(ExpenseService expenseService){
        this.expenseService = expenseService;
    }

Add the following methods then

 @GetMapping("/from-db")
    @ResponseBody
    public List<Expense> getExpensesFromDB(){
        return expenseService.getAll();
    }

    @PostMapping("/create-expense")
    @ResponseBody
    public Expense createExpenseInDB(@RequestBody Expense expense){
        return expenseService.save(expense);
    }

Build the app again with mvn clean install command

Run with

java -jar -Dspring.profiles.active=mysql target/spring-boot-personal-expense-api-0.0.1-SNAPSHOT.jar

Run output

2024-05-13T10:19:07.578-05:00  INFO 33340 --- [spring-boot-personal-expense-api] [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
Hibernate: drop table if exists personal_expense_tracker
Hibernate: create table personal_expense_tracker (expense_amount decimal(38,2), expense_id integer not null auto_increment, expense_name varchar(255), primary key (expense_id)) engine=InnoDB
2024-05-13T10:19:07.780-05:00  INFO 33340 --- [spring-boot-personal-expense-api] [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2024-05-13T10:19:08.020-05:00  WARN 33340 --- [spring-boot-personal-expense-api] [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2024-05-13T10:19:08.503-05:00  INFO 33340 --- [spring-boot-personal-expense-api] [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path '/actuator'
2024-05-13T10:19:08.572-05:00  INFO 33340 --- [spring-boot-personal-expense-api] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 3091 (http) with context path ''
2024-05-13T10:19:08.585-05:00  INFO 33340 --- [spring-boot-personal-expense-api] [           main] .SpringBootPersonalExpenseApiApplication : Started SpringBootPersonalExpenseApiApplication in 5.485 seconds (process running for 5.955)

With the property

spring.jpa.hibernate.ddl-auto=create

Table DDL

create table expense_tracker.personal_expense_tracker
(
    expense_amount decimal(38, 2) null,
    expense_id     int auto_increment
        primary key,
    expense_name   varchar(255)   null
);

Tables get created automatically in the database

plot

Download a REST API Client called PostMan

Try to create a new expense

plot
plot

Body

plot
plot

Create another

plot
plot

Get All

plot
plot