Build REST CRUD APIs with Kotlin, Spring Boot and MyBatis

Hello everyone, today we will learn how to develop REST-style CRUD APIs with Spring Boot, Kotlin, MyBatis, and H2 Database. You can download the source code from our Github repository.

What is MyBatis?

MyBatis is a 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.

Some of the features of MyBatis:
  • In memory object filtering
  • Fortifies clustering and simultaneous access by other applications without loss of transaction integrity
  • Query Caching - Built-in support
  • Fortifies disconnected operations
  • Support for Remoting. Distributed Objects.

Technologies used :

  • Spring Boot 2.5.4
  • Spring  5.3.9
  • MyBatis 
  • Kotlin
  • Gradle

After completing this tutorial what we will build? 

We will build REST API  CRUD features: 

Project Directory



Gradle Build(build.gradle.kts)

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id("org.springframework.boot") version "2.5.4"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.5.21"
kotlin("plugin.spring") version "1.5.21"
}

group = "com.knf.dev.demo"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

repositories {
mavenCentral()
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0")
runtimeOnly("com.h2database:h2")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}

tasks.withType<Test> {
useJUnitPlatform()
}


Database Setup

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 integer not null,
firstName varchar(255) not null,
lastName varchar(255) not null,
emailId varchar(255) not null,
primary key(id)
);


Create User Model

package com.knf.dev.demo.model

class User {
var id: Long = 0
var firstName: String? = null
var lastName: String? = null
var emailId: String? = null

constructor() {}
constructor(id: Long, firstName: String?, lastName: String?,
                                emailId: String?) : super() {
this.id = id
this.firstName = firstName
this.lastName = lastName
this.emailId = emailId
}
}


Create User MyBatis Repository

package com.knf.dev.demo.repository

import com.knf.dev.demo.model.User
import org.apache.ibatis.annotations.*

@Mapper
interface UserRepository {
@Select("select * from users")
fun findAll(): List<User?>?

@Select("SELECT * FROM users WHERE id = #{id}")
fun findById(id: Long): User?

@Delete("DELETE FROM users WHERE id = #{id}")
fun deleteById(id: Long): Int

@Insert(
"INSERT INTO users(id, firstName, lastName,emailId) " +
" VALUES (#{id}, #{firstName}, #{lastName}, #{emailId})"
)
fun insert(user: User?): Int

@Update(
"Update users set firstName=#{firstName}, " +
" lastName=#{lastName}, emailId=#{emailId} where id=#{id}"
)
fun update(user: User?): Int
}


Global Exception Handling

UserIdNotFoundException

package com.knf.dev.demo.exception

import java.lang.RuntimeException

class UserIdNotFoundException : RuntimeException("User Id Not Found")



UserIdAlreadyExistException

package com.knf.dev.demo.exception

import java.lang.RuntimeException

class UserIdAlreadyExistException : RuntimeException("User Id Already Exist")



GlobalExceptionHandler

package com.knf.dev.demo.exception

import org.springframework.web.bind.annotation.ControllerAdvice
import com.knf.dev.demo.exception.UserIdNotFoundException
import org.springframework.web.context.request.WebRequest
import org.springframework.http.ResponseEntity
import java.time.LocalDateTime
import org.springframework.http.HttpStatus
import com.knf.dev.demo.exception.UserIdAlreadyExistException
import org.springframework.web.bind.annotation.ExceptionHandler
import java.lang.Exception

@ControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(UserIdNotFoundException::class)
fun globalExceptionHandler(ex: Exception, request: WebRequest?):
ResponseEntity<CustomErrorResponse> {
val errors = CustomErrorResponse()
errors.timestamp = LocalDateTime.now()
errors.error = ex.message
errors.status = HttpStatus.NOT_FOUND.value()
return ResponseEntity<CustomErrorResponse>(errors, HttpStatus.NOT_FOUND)
}

@ExceptionHandler(UserIdAlreadyExistException::class)
fun globalExceptionHandler2(ex: Exception, request: WebRequest?):
ResponseEntity<CustomErrorResponse> {
val errors = CustomErrorResponse()
errors.timestamp = LocalDateTime.now()
errors.error = ex.message
errors.status = HttpStatus.INTERNAL_SERVER_ERROR.value()
return ResponseEntity<CustomErrorResponse>(errors,
HttpStatus.INTERNAL_SERVER_ERROR)
}
}


CustomErrorResponse

package com.knf.dev.demo.exception

import com.fasterxml.jackson.annotation.JsonFormat
import java.time.LocalDateTime

class CustomErrorResponse {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
var timestamp: LocalDateTime? = null
var status = 0
var error: String? = null
}



Create User Rest Controller

package com.knf.dev.demo.controller

import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.beans.factory.annotation.Autowired
import com.knf.dev.demo.repository.UserRepository
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import com.knf.dev.demo.exception.UserIdAlreadyExistException
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.http.ResponseEntity
import com.knf.dev.demo.exception.UserIdNotFoundException
import com.knf.dev.demo.model.User
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.DeleteMapping
import java.util.HashMap

@RestController
@RequestMapping("/api/v1")
class UserController {
@Autowired
private val userRepository: UserRepository? = null

// get all users
@get:GetMapping("/users")
val allUsers: List<User?>?
get() = userRepository!!.findAll()

// create user rest API
@PostMapping("/users")
fun createUser(@RequestBody user: User): User? {
return if (userRepository!!.findById(user.id) == null) {
val id = userRepository.insert(user)
userRepository.findById(user.id)
} else {
throw UserIdAlreadyExistException()
}
}

// get user by id rest api
@GetMapping("/users/{id}")
fun getUserById(@PathVariable id: Long?): ResponseEntity<User> {
val user = userRepository!!.findById(id!!) ?: throw UserIdNotFoundException()
return ResponseEntity.ok(user)
}

// update user rest api
@PutMapping("/users/{id}")
fun updateUser(
@PathVariable id: Long?,
@RequestBody userDetails: User
): ResponseEntity<User> {
if (userRepository!!.update(
User(
id!!, userDetails.firstName,
userDetails.lastName, userDetails.emailId
)
) == 0
) {
throw UserIdNotFoundException()
}
return ResponseEntity.ok(userRepository.findById(id))
}

// delete user rest api
@DeleteMapping("/users/{id}")
fun deleteUser(@PathVariable id: Long?): ResponseEntity<Map<String, Boolean>> {
userRepository!!.deleteById(id!!)
val response: MutableMap<String, Boolean> = HashMap()
response["deleted"] = java.lang.Boolean.TRUE
return ResponseEntity.ok(response)
}
}


Spring Boot Main Driver

package com.knf.dev.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class KotlinSpringbootMybatisCrudExampleApplication

fun main(args: Array<String>) {
runApplication<KotlinSpringbootMybatisCrudExampleApplication>(*args)
}


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


Testing APIs using Postman

Create a User:

Fetch all the users:

Get user by id:

Update User:

Delete User:




Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete