Build REST CRUD APIs with Spring Boot, MyBatis(XML Mapping), & PostgreSQL
In this section, we will learn how to develop a REST-style web service with Spring Boot, MyBatis(XML Mapping) and PostgreSQL. GitHub repository link is provided at the end of this tutorial. You can download the source code.
Technologies used:
- Spring Boot 3.0.1
- MyBatis 3
- Java 17
- Maven
- PostgreSQL driver and database
These are APIs that Spring Boot backend App will export:
Creating a simple spring boot web application:
Then, click on the Generate button. When we click on the Generate button, it starts packing the project in a .zip(spring-boot-postgresql-mybatis-xml-crud) file and downloads the project. Then, Extract the Zip file.
Final Project directory:
Create database.
CREATE DATABASE testdb;
pom.xml
<?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>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>spring-boot-postgresql-mybatis-xml-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-postgresql-mybatis-xml-crud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</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>3.0.0</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</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>
schema.sql
CREATE TABLE IF NOT EXISTS users
(
id SERIAL PRIMARY KEY,
first_name varchar(255) not null,
last_name varchar(255) not null,
email_id varchar(255) not null
);
application.properties
mybatis.mapper-locations is a key configuration. It specifies the path to load the xml file for the mapper interface (wildcards can be used).
Use mybatis.type-aliases-package to specify the POJO scan package so that mybatis automatically scans for custom POJOs.
spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=postgres
spring.datasource.password=root
spring.sql.init.mode=always
#Configure the xml Mapping path
mybatis.mapper-locations=classpath:mapper/UserMapper.xml
#Configure entity category names
mybatis.type-aliases-package=com.knf.dev.demo.model
Creating User Bean
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 Repository
package com.knf.dev.demo.repository;
import com.knf.dev.demo.model.User;
import java.util.List;
import java.util.Optional;
public interface UserRepository {
List<User> findAll();
Optional<User> findById(Integer id);
int deleteById(Integer id);
int insert(User user);
int update(User user);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.knf.dev.demo.repository.UserRepository">
<resultMap type="user" id="result">
<id property="id" column="id" />
<result property="firstName" column="first_name" />
<result property="lastName" column="last_name" />
<result property="emailId" column="email_id" />
</resultMap>
<select id="findAll" resultType="user" resultMap="result">
SELECT * FROM users
</select>
<insert id="insert" parameterType="user"
keyProperty="id" useGeneratedKeys="true">
INSERT INTO users(first_name, last_name, email_id)
VALUES(#{firstName}, #{lastName}, #{emailId})
</insert>
<update id="update" parameterType="user">
UPDATE users SET first_name = #{firstName},
email_id = #{emailId}, last_name = #{lastName}
WHERE id = #{id}
</update>
<delete id="deleteById" parameterType="int">
DELETE FROM users WHERE id = #{id}
</delete>
<select id="findById" parameterType="int"
resultType="user" resultMap="result">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
-Exception handling
Create ResourceNotFoundException
package com.knf.dev.demo.exception;
public class ResourceNotFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) {
super(message);
}
}
Create GlobalExceptionHandler
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);
}
}
Create 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;
}
}
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;
}
}
Run the application - Application.java
package com.knf.dev.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.knf.dev.demo.repository")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
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
OR
Run this Spring boot application from
- IntelliJ IDEA IDE by right click - Run 'Application.main()'
- Eclipse/STS - You can right click the project or the Application.java file and run as java application or Spring boot 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
OR
Run this Spring boot application from
- IntelliJ IDEA IDE by right click - Run 'Application.main()'
- Eclipse/STS - You can right click the project or the Application.java file and run as java application or Spring boot application.