How to build a Spring Boot REST API From Scratch
Google Spring Initializr
plot
Click on the first link
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 :
See below
plot
Create Java Packages
Right-click on the com.rollingstone package and choose
plot
Create model package
plot
Create the controller package
Create Model Class
Expense.java
plot
Lets create a Model Class
Right-click on the com.rollingstone.model package and choose
Enter the following attributes / fields
plot
Image 9
plot
Choose Code -> Generate -> Getters and Setters
plot
Choose Code -> Generate -> Constructor
plot
Choose Code -> Generate -> Constructor
Choose Select None to create a Blank Constructor
plot
Choose Code -> Generate -> toString
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
Build Success
plot
Now enter
java -jar target/spring-boot-personal-expense-api-0.0.1-SNAPSHOT.jar
Application is Running
plot
Open the resources folder and double-click on application.properties
file
plot
aa
plot
aa
plot
aa
plot
Image 19
plot
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
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
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>
MySQL is a popular database with the driver
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</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
Download The First One and Install By Double-Clicking the
Installer
The installation will create a local MySQL Database we can use
plot
How to update properties for MySQL
Before we proceed, we wqould also need a MySQL Client
Application
plot
Download Datagrip
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