Build REST CRUD APIs with Spring Boot and MyBatis
Hello everyone, here we will learn how to develop REST-style CRUD APIs with Spring Boot, MyBatis, and H2 Database (using annotations). You can download the source code from our Github repository.
Technologies used:
- Spring Boot 2.5.4
- MyBatis 2.2.0
- Java 11
- Maven 3+
MyBatis is a first-class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results. MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records. More Info...
These are APIs that Spring Boot backend App will export:
Project Directory:
Maven dependencies [pom.xml]
Puts mybatis-spring-boot-starter, h2 dependencies.
<?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
https://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.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>springboot-mybatis-crud-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-mybatis-crud-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</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>
Database Setup - schema.sql
We will create a table called users with a few simple columns.
We can initialize a schema by creating a schema.sql file in the resources.
create table users
(
id IDENTITY NOT NULL PRIMARY KEY ,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
email_id VARCHAR(255) NOT NULL
);
Create User Model
package com.knf.dev.demo.model;
public class User {
private Integer id;
private String firstName;
private String lastName;
private String emailId;
public User() {
}
public User(Integer id,String firstName,
String lastName, String emailId) {
super();
this.id=id;
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
}
Create User MyBatis Repository
@Mapper: indicates that this is a MyBatis mapper class.
@Insert, @Select, @Update, @Delete: those annotations represent SQL statements to be executed by calling annotated methods.
@Results: A list of Result mappings that contain details of how a particular result column is mapped to a property or field.
@Result: A single result mapping between a column and a property or field.
package com.knf.dev.demo.repository;
import com.knf.dev.demo.model.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Optional;
@Mapper
public interface UserRepository {
@Select("select * from users")
@Results({
@Result(property = "firstName", column = "first_name"),
@Result(property = "lastName", column = "last_name"),
@Result(property = "emailId", column = "email_id")
})
List<User> findAll();
@Select("SELECT * FROM users WHERE id = #{id}")
@Results({
@Result(property = "firstName", column = "first_name"),
@Result(property = "lastName", column = "last_name"),
@Result(property = "emailId", column = "email_id")
})
Optional<User> findById(Integer id);
@Delete("DELETE FROM users WHERE id = #{id}")
int deleteById(Integer id);
@Insert("INSERT INTO users(first_name, last_name, email_id) " +
" VALUES (#{firstName}, #{lastName}, #{emailId})")
int insert(User user);
@Update("Update users set first_name=#{firstName}, " +
" last_name=#{lastName}, email_id=#{emailId} where id=#{id}")
int update(User user);
}
Create User Rest Controller
package com.knf.dev.demo.controller;
import com.knf.dev.demo.exception.ResourceNotFoundException;
import com.knf.dev.demo.model.User;
import com.knf.dev.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1")
public class UserController {
@Autowired
private UserRepository userRepository;
// get all users
@GetMapping("/users")
public List<User> getAllUsers()
{
return userRepository.findAll();
}
// create user rest API
@PostMapping("/users")
public Map<String, Boolean> createUser(@RequestBody User user) {
Map<String, Boolean> response = new HashMap<>();
Boolean bool = userRepository.insert(user) > 0 ?
response.put("created", Boolean.TRUE) :
response.put("created", Boolean.FALSE);
return response;
}
// get user by id rest api
@GetMapping("/users/{id}")
public User findUserById(@PathVariable Integer id) {
User user = userRepository.findById(id).
orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));
return user;
}
// update user rest api
@PutMapping("/users/{id}")
public Map<String, Boolean> updateUser(@PathVariable Integer id,
@RequestBody User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));
userDetails.setId(id);
Map<String, Boolean> response = new HashMap<>();
Boolean bool = userRepository.update(userDetails) > 0 ?
response.put("updated", Boolean.TRUE) :
response.put("updated", Boolean.FALSE);
return response;
}
// delete user rest api
@DeleteMapping("/users/{id}")
public Map<String, Boolean> deleteUser
(@PathVariable Integer id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));
Map<String, Boolean> response = new HashMap<>();
Boolean bool = userRepository.deleteById(user.getId()) > 0 ?
response.put("deleted", Boolean.TRUE) :
response.put("deleted", Boolean.FALSE);
return response;
}
}
-Global Exception handling
ResourceNotFoundException.java
package com.knf.dev.demo.exception;
public class ResourceNotFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) {
super(message);
}
}
GlobalExceptionHandler
Spring supports exception handling by a Global Exception Handler (@ExceptionHandler) with Controller Advice (@ControllerAdvice). This enables a mechanism that makes ResponseEntity work with the type safety and flexibility of @ExceptionHandler.
package com.knf.dev.demo.exception;
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;
import java.time.LocalDateTime;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<CustomErrorResponse>
globalExceptionHandler(Exception ex, WebRequest request) {
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND);
}
}
CustomErrorResponse
package com.knf.dev.demo.exception;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
public class CustomErrorResponse {
@JsonFormat(shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd hh:mm:ss")
private LocalDateTime timestamp;
private int status;
private String error;
public LocalDateTime getTimestamp()
{
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp)
{
this.timestamp = timestamp;
}
public int getStatus()
{
return status;
}
public void setStatus(int status)
{
this.status = status;
}
public String getError()
{
return error;
}
public void setError(String error)
{
this.error = error;
}
}
Spring Boot Main Driver
The Spring Boot application's main class contains a public static void main() method that starts up the Spring ApplicationContext.
package com.knf.dev.demo;
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);
}
}
Download the complete source code - click here
Local Setup and Run the application
Step1: Download or clone the source code from GitHub to a local machine - Click here
Step 2: mvn clean install
Step 3: Run the Spring Boot application - mvn spring-boot:run
Step1: Download or clone the source code from GitHub to a local machine - Click here
Step 2: mvn clean install
Step 3: Run the Spring Boot application - mvn spring-boot:run
Add User:
Fetch all User:
Get User by ID:
Update User:
Delete User by ID:
More related topics,