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
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
More related topics,