Create Rest API using Springboot and MySql
8 min readJan 2, 2023
Introduction
On this article we will learn how to create REST API using Springboot, that apis will have CRUD function for connecting and storing data to MySql database. take a look image below for simple discribe all the apis.
there are list of apis we will created and test using Postman client.
Requirements
- Maven 3+
- Java 8+
- IDE (Intelij Idea o Eclipse)
- Docker
- MySql Database (container)
- Postman (API Test)
Overviews
- Create MySql Container
- Generate Springboot project
- Add MySql connection on application.properties
- Create Entity, DTO, Repo, Service, Validator, Controller
- Test the Rest API using Postman
Create MySql Container
- First we must create the MySql container as a Database server for store the data. On this article we will use the Docker for simplify the installation. If you already have Docker on your machine, you just create the file docker-compose.yml and than you write the syntax like this :
version: "3"
services: mysql:
container_name: server_mysql
image: mysql:5.7
environment:
MYSQL_DATABASE: 'db'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_TCP_PORT: 3300
ports:
- 3300: 3300
expose:
- 3300
volumes:
- mysql:/var/lib/mysql
networks:
- networknetworks:
network:
driver: bridgevolumes:
mysql:
driver: local
- And then open the cmd or terminal, tipe syntax like this
docker-compose up -id
- The MySql container already created. if you want checking the container status , you just tipe syntax like below:
docker container ls
and the result like this
Generate Springboot project
- Next we create the springboot project, on link : https://start.spring.io/
- add dependencies like below
- Then click “Generate”. You will find a generated .zip file, extract it and then open the extracted folder in your favorite IDE.
Add MySql connection on application.properties
- Open file application.properties, and than add the MySql connection into that
#server port http
server.port=8181
#mysql config
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=false
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialectspring.datasource.url=jdbc:mysql://localhost:3300/database
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Controller, Service, Repo Components
- Before we starting create the apis, we must knows the three important components of springboot framework, that are Controller, Service, Repository. take look the image below for simple describe the components.
- Controller is a class for handle the HTTP Method, request, response and all about HTTP.
- Service is a class for handle the bussines logic, validator, case , ect
- Repository is a class for handle the all function for manipulate data to Database such as (save, update, delete, select, etc)
so we already knows the mean of three important component on springboot framework. lets continues for craeted the apis.
Create UserEntity
- Create the User class for maping with the User table from MySql database, the User class used when holding the value from User Repo when all function CRUD be called by Service class, take look code like below
package com.deni.app.module.user.entity;
import lombok.*;import javax.persistence.*;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String username;
String password;
String roles;
String permissions ;
Integer blocked;
int active;
}
- Annotation Descriptions:
- Getter is used for generate variable getter function
- Setter is used for generate variable setter function
- AllArgsConstructor is used for auto generate all args contructior function
- NoArgsConstructor is used for auto generate no arg constructor function
- Builder is used for generate auto builder function
- Entity is used to annotate that the class is an entity in the database.
- Table is used to define the table name
- Id is used for define the id of table
- GeneratedValued is used to generate strategies for the values of primary keys.
Create UserRepo
- Create User Repository interface extending JPA Repository
package com.deni.app.module.user.repo;
import com.deni.app.module.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepo extends JpaRepository<User, Long> {
User findByUsername(String username);}
- Annotation Descriptions:
- Repository is used for register that class as a Repository on Spring
- UserRepo will have methods for CRUD operations inherite from JpaRepository, and writing any SQL query is not needed.
Create UserDTO
- Create the UserDTO class for maping with the Users Entity, the UserDTO class used when holding the value from http request body and http response body.
package com.deni.app.module.user.dto;
import com.deni.app.module.user.entity.User;
import lombok.*;import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserDTO { Long id;
String username;
String password;
String roles;
String permissions;
Integer blocked;
Integer active;
public List<UserDTO> listOf(List<User> list) { List<UserDTO> result = new ArrayList<>();
for (User entity : list) {
result.add(of(entity));
}
return result;
} public UserDTO of(User entity) {
return UserDTO.builder()
.id(entity.getId())
.password(entity.getPassword())
.username(entity.getUsername())
.permissions(entity.getPermissions())
.roles(entity.getRoles())
.active(entity.getActive())
.blocked(entity.getBlocked())
.build();
}
}
Create UserService Interface
- create UserService interface for contract the all function CRUD.
package com.deni.app.module.user.service;
import com.deni.app.module.user.dto.UserDTO;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
@Service
public interface UserService {
public ResponseEntity save(UserDTO requestDTO);
public ResponseEntity update(Long id, UserDTO requestDTO); public ResponseEntity delete(Long id);
public ResponseEntity getAll();
public ResponseEntity findById(Long id);
public ResponseEntity deleteAll();
}
Create UserService Implementation
- create UserServiceImpl class for implementation all function from UserService.
package com.deni.app.module.user.service;
import com.deni.app.common.constants.Messages;
import com.deni.app.common.controller.ResponseHandler;
import com.deni.app.common.validator.Validator;
import com.deni.app.module.user.dto.UserDTO;
import com.deni.app.module.user.entity.User;
import com.deni.app.module.user.repo.UserRepo;
import com.deni.app.module.user.validator.UserValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;
@Service
public class UserServiceImpl implements UserService { @Autowired
UserRepo userRepo; @Autowired
UserValidator userValidator; // for unitest (mocking)
public UserServiceImpl(UserValidator userValidator, UserRepo userRepo) {
this.userValidator = userValidator;
this.userRepo = userRepo;
} @Override
@Transactional
public ResponseEntity save(UserDTO requestDTO) {
Validator validator = userValidator.requestValidator(requestDTO);
if (validator.isSuccess()) {
validator = userValidator.duplicateValidator(requestDTO.getUsername());
if (validator.isSuccess()) { User entity = User.builder()
.username(requestDTO.getUsername())
.password(requestDTO.getPassword())
.permissions(requestDTO.getPermissions())
.roles(requestDTO.getRoles())
.active(1)
.blocked(0)
.build(); User save = userRepo.save(entity); // OK
return ResponseHandler.createHttpResponse(
Messages.MSG_SAVE_SUCCESS,
new UserDTO().of(save),
HttpStatus.OK); } else {
return ResponseHandler.createHttpResponse(
validator.getMessage(),
requestDTO,
HttpStatus.BAD_REQUEST);
}
} else {
return ResponseHandler.createHttpResponse(
validator.getMessage(),
requestDTO,
HttpStatus.BAD_REQUEST);
}
} @Override
@Transactional
public ResponseEntity update(Long id, UserDTO requestDTO) {
Validator validator = userValidator.requestValidatorForUpdate(requestDTO);
if (validator.isSuccess()) {
Optional<User> optional = userRepo.findById(id);
if (optional.isPresent()) {
User update = (User) optional.get();
update.setUsername(requestDTO.getUsername()); update.setPermissions(requestDTO.getPermissions());
update.setRoles(requestDTO.getRoles());
update.setBlocked(requestDTO.getBlocked());
update.setActive(requestDTO.getActive());
update = userRepo.save(update);
// OK
return ResponseHandler.createHttpResponse(
Messages.MSG_UPDATE_SUCCESS,
new UserDTO().of(update),
HttpStatus.OK); } else {
return ResponseHandler.createHttpResponse(
Messages.MSG_DATA_NOT_FOUND,
requestDTO,
HttpStatus.NOT_FOUND);
}
} else {
return ResponseHandler.createHttpResponse(
validator.getMessage(),
requestDTO,
HttpStatus.BAD_REQUEST);
}
}
@Override
@Transactional
public ResponseEntity getAll() {
List<User> list = userRepo.findAll();
if (!list.isEmpty()) { // OK
return ResponseHandler.createHttpResponse(
Messages.MSG_DATA_FOUND,
new UserDTO().listOf(list),
HttpStatus.OK);
} else {
return ResponseHandler.createHttpResponse(
Messages.MSG_DATA_NOT_FOUND,
"",
HttpStatus.NOT_FOUND);
}
}
@Override
@Transactional
public ResponseEntity findById(Long id) {
Optional<User> optional = userRepo.findById(id);
if (optional.isPresent()) { // OK
return ResponseHandler.createHttpResponse(
Messages.MSG_DATA_FOUND,
new UserDTO().of(optional.get()),
HttpStatus.OK); } else {
return ResponseHandler.createHttpResponse(
Messages.MSG_DATA_NOT_FOUND,
"",
HttpStatus.NOT_FOUND);
}
}
@Override
@Transactional
public ResponseEntity delete(Long id) {
Optional<User> optional = userRepo.findById(id);
if (optional.isPresent()) {
userRepo.delete(optional.get()); // OK
return ResponseHandler.createHttpResponse(
Messages.MSG_DELETE_SUCCESS,
optional.get(),
HttpStatus.OK); } else {
return ResponseHandler.createHttpResponse(
Messages.MSG_DATA_NOT_FOUND,
"",
HttpStatus.NOT_FOUND);
}
} @Override
@Transactional
public ResponseEntity deleteAll() {
userRepo.deleteAll(); // OK
return ResponseHandler.createHttpResponse(
Messages.MSG_DELETE_SUCCESS,
"",
HttpStatus.OK);
}
}
Create UserValidator
- UserValidator class is used for validate all variable from http request, this class will checked the conditions like, null value, empty value, zero value, etc, and if validator.isSuccess() == false, so the service will return to the controller with response is bad request
- craete UserValidator class for handling UserDTO data, come from http request body.
package com.deni.app.module.user.validator;
import com.deni.app.common.constants.Messages;
import com.deni.app.common.validator.Validator;
import com.deni.app.module.user.dto.UserDTO;
import com.deni.app.module.user.entity.User;
import com.deni.app.module.user.repo.UserRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
public class UserValidator { @Autowired
UserRepo repo; public Validator duplicateValidator(String uniqueKey) {
User entity = repo.findByUsername(uniqueKey);
if (entity != null) {
return new Validator().no(String.format(Messages.MSG_DATA_ALREADY_EXIST, entity.getUsername()));
} else {
return new Validator().yes(Messages.MSG_DATA_IS_AVAILABLE);
}
} public Validator requestValidator(UserDTO dto) {
List<String> message = new ArrayList<>(); if (dto.getUsername() == null) {
message.add(String.format(Messages.MSG_FIELD_CANNOT_BE_NULL, "username"));
} else {
if (dto.getUsername().isEmpty()) {
message.add(String.format(Messages.MSG_FIELD_CANNOT_BE_EMPTY, "username"));
}
}
if (dto.getPassword() == null) {
message.add(String.format(Messages.MSG_FIELD_CANNOT_BE_NULL, "password"));
} else {
if (dto.getPassword().isEmpty()) {
message.add(String.format(Messages.MSG_FIELD_CANNOT_BE_EMPTY, "password"));
}
}
if (message.isEmpty()) {
return new Validator().yes();
} else {
String result = "";
for (String str : message) {
if (result.isEmpty()) {
result = str;
} else {
result += ", " + str;
}
}
return new Validator().no(result);
}
}
public Validator requestValidatorForUpdate(UserDTO dto) {
List<String> message = new ArrayList<>(); if (dto.getUsername() == null) {
message.add(String.format(Messages.MSG_FIELD_CANNOT_BE_NULL, "username"));
} else {
if (dto.getUsername().isEmpty()) {
message.add(String.format(Messages.MSG_FIELD_CANNOT_BE_EMPTY, "username"));
}
}
if (dto.getActive() == null) {
message.add(String.format(Messages.MSG_FIELD_CANNOT_BE_NULL, "active"));
} else {
if (dto.getActive() > 1) {
message.add(String.format(Messages.MSG_FIELD_REQUIRED_ARE_1_OR_0, "active"));
}
} if (dto.getBlocked() == null) {
message.add(String.format(Messages.MSG_FIELD_CANNOT_BE_NULL, "active"));
} else {
if (dto.getBlocked() > 1) {
message.add(String.format(Messages.MSG_FIELD_REQUIRED_ARE_1_OR_0, "active"));
}
}
if (message.isEmpty()) {
return new Validator().yes();
} else {
String result = "";
for (String str : message) {
if (result.isEmpty()) {
result = str;
} else {
result += ", " + str;
}
}
return new Validator().no(result);
}
}}
Create UserController class
- Create Rest User Controllers class which contains all REST API endpoints for CRUD operations.
package com.deni.app.module.user.controller;
import com.deni.app.common.controller.Response;
import com.deni.app.module.user.dto.UserDTO;
import com.deni.app.module.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/user")
public class UserController { @Autowired
UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping(value = "/save")
public ResponseEntity<Response> save(@RequestBody UserDTO request) {
return userService.save(request);
}
@PutMapping(value = "/update/{id}")
public ResponseEntity<Response> update(@PathVariable Long id,
@RequestBody UserDTO request) {
return userService.update(id, request);
}
@DeleteMapping(value = "/delete/{id}")
public ResponseEntity<Response> delete(@PathVariable Long id) {
return userService.delete(id);
}
@DeleteMapping(value = "/delete/all")
public ResponseEntity<Response> deleteAll() {
return userService.deleteAll();
}
@GetMapping(value = "/list")
public ResponseEntity<Response> getAll() {
return userService.getAll();
}
@GetMapping("/find/{id}")
public ResponseEntity<Response> findById(@PathVariable Long id) {
return userService.findById(id);
}}
- Annotation Descriptions:
- RestController is used for registered the UserController as a Controller to the spring
- RequestMapping is used for define a url end point
- Autowired is used for initialze the UserService
- PostMapping is used for define the http method POST
- PutMapping is used for define the http method PUT
- DeleteMapping is used for define the http method DELETE
- GetMapping is used for define the http method GET
Run the Springboot Application
- Run Springboot application , and make sure the app is running .
Test the Rest API using Postman
- Test API add user on http://localhost:8181/api/user/save
- Test API update user on http://localhost:8181/api/user/update/{id}
- Test API find user by id on http://localhost:8181/api/user/find/{id}
- Test API get all user on http://localhost:8181/api/user/list
- Test API delete user on http://localhost:8181/api/user/delete/{id}
Conclusion
Finally we already learn how to create RESTful API using Springboot and MySql Database, with case CRUD functions.
Happy Learning!