Kotlin + Spring Boot + Spring Data JPA - CRUD example

Hello everyone, Today we will learn how to develop a Kotlin + Spring Boot CRUD web application, using Thymeleaf view, H2DB, and Spring Data JPA.


User Interface


Technologies used :

  • Spring Boot 2.3.7.RELEASE
  • Spring  5.2.12.RELEASE
  • Kotlin 2.11.3
  • Thymeleaf 5.3.0.11.RELEASE
  • Hibernate 5.4.25.Final
  • H2DB
  • Maven

After completing this tutorial what we will build? 

We will build a full-stack web application that is a basic User Management Application with CRUD features: 
  • Create User  
  • List User  
  • Update User  
  • Delete User

Project Structure



Maven[pom.xml]

A Project Object Model or POM is an XML file that contains information about the project and configuration details used by Maven to build the project.
<?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.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev</groupId>
<artifactId>kotlin_springb_h2db_thymeleaf_crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>kotlin_springb_h2db_thymeleaf_crud</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<kotlin.version>1.3.72</kotlin.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
<plugin>jpa</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>



Create the User Entity

The @Entity annotation specifies that the class is an entity and is mapped to a database table. The @Id annotation specifies the primary key of an entity and the @GeneratedValue provides for the specification of generation strategies for the values of primary keys.

package com.knf.dev.entity

import javax.persistence.*

@Entity
@Table(name = "user")
data class User(

@Id @GeneratedValue(strategy = GenerationType.AUTO)
val id: Long = 0,
val firstName: String,
val lastName: String,
val email: String
)



Create the User Repository

@Repository annotation is used to indicate that the class provides the mechanism for storage, retrieval, search, update and delete operation on objects.

package com.knf.dev.repository

import com.knf.dev.entity.User
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface UserRepository : JpaRepository<User, Long>



Create the User Controller 

The @RestController annotation was introduced in Spring 4.0 to simplify the engendering of RESTful web services. It's a convenience annotation that combines @Controller and @ResponseBody. @RequestMapping annotation maps HTTP requests to handler methods of MVC and REST controllers.
package com.knf.dev.controller

import com.knf.dev.entity.User
import com.knf.dev.repository.UserRepository
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.*
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import java.util.*
import org.springframework.web.bind.annotation.PathVariable


@Controller
class UserController(private val userRepository: UserRepository) {

@GetMapping("/")
fun getAllUsers(model: Model): String? {
val list: List<User> = userRepository.findAll()
model.addAttribute("users", list)
return "list-users"
}

@PostMapping(path = arrayOf("/createUser"))
fun createNewUser(@ModelAttribute user: User): String {
userRepository.save(user)
return "redirect:/";
}

@GetMapping(path = ["/add"])
fun addUserById(): String? {
return "add-user"
}

@GetMapping(path = ["/edit/{id}"])
fun editUserById(model: Model, @PathVariable("id") id: Long): String? {
model.addAttribute("user", userRepository.findById(id))
return "edit-user"
}

@PostMapping(path = ["/editUser"])
fun editUser(@ModelAttribute user: User): String? {
userRepository.save(user)
return "redirect:/"
}

@GetMapping(path = ["/delete/{id}"])
fun deleteUserById(@PathVariable("id") id: Long): String? {
userRepository.deleteById(id)
return "redirect:/"
}
}



Spring Boot Main

The @SpringBootApplication annotation is a convenience annotation that combines the @EnableAutoConfiguration, @Configuration and the @ComponentScan annotations.
package com.knf.dev

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

@SpringBootApplication
class KotlinSpringbH2DBThymeleafApplication

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



add-user.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Add User</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="all" href="../../
bootstrap.min.css" th:href="@{/bootstrap.min.css}"/>
<link rel="stylesheet" href="https://use.fontawesome.com
/releases/v5.4.1/css/all.css">
</head>

<body>
<div class="container my-5">
<h3>Add User</h3>
<div class="card">
<div class="card-body">
<div class="col-md-10">
<form action="#" th:action="@{/createUser}" th:object="${user}"
method="post">
<div class="row">
<div class="form-group col-md-8">
<label for="firstName" class="col-form-label">
First Name</label>
<input type="text" name="firstName"
class="form-control" id="firstName"
placeholder="First Name"
/>
</div>
<div class="form-group col-md-8">
<label for="lastName" class="col-form-label">
Last Name</label>
<input type="text" name="lastName"
class="form-control" id="lastName"
placeholder="Last Name"/>
</div>
<div class="form-group col-md-8">
<label for="email" class="col-form-label">Email</label>
<input type="text" name="email"
class="form-control" id="email"
placeholder="Email Id"/>
</div>
<div class="col-md-6">
<input type="submit" class="btn btn-success"
value=" Submit ">
</div>
<input type="hidden" id="id">
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>



edit-user.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Add User</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="all" href="../../
bootstrap.min.css" th:href="@{/bootstrap.min.css}"/>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/
v5.4.1/css/all.css">
</head>

<body>
<div class="container my-5">
<h3>Update User</h3>
<div class="card">
<div class="card-body">
<div class="col-md-10">
<form action="#" th:action="@{/editUser}" th:object="${user}"
method="post">
<div class="row">
<div class="form-group col-md-8">
<label for="firstName" class="col-form-label">
First Name</label>
<input type="text" th:field="*{firstName}"
class="form-control" id="firstName"
placeholder="First Name"
/>
</div>
<div class="form-group col-md-8">
<label for="lastName" class="col-form-label">
Last Name</label>
<input type="text" th:field="*{lastName}"
class="form-control" id="lastName"
placeholder="Last Name"/>
</div>
<div class="form-group col-md-8">
<label for="email" class="col-form-label">Email</label>
<input type="text" th:field="*{email}"
class="form-control" id="email"
placeholder="Email Id"/>
</div>
<div class="col-md-6">
<input type="submit" class="btn btn-success"
value=" Submit ">
</div>
<input type="hidden" th:field="*{id}" id="id">
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>



list-users.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>All Users</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="all"
href="../../bootstrap.min.css" th:href="@{/bootstrap.min.css}" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases
/v5.4.1/css/all.css">
</head>

<body>
<div class="container my-2">
<div class="card">
<div class="card-body">
<div th:switch="${users}" class="container my-5">
<p class="my-5">
<a href="/add" class="btn btn-success">
<i class="fas fa-user-plus ml-2"> Add User </i></a>
</p>
<div class="col-md-10">
<h2 th:case="null">No record found !!</h2>
<div th:case="*">
<table class="table table-striped table-responsive-md">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.firstName}"></td>
<td th:text="${user.lastName}"></td>
<td th:text="${user.email}"></td>
<td><a th:href="@{/edit/{id}(id=${user.id})}"
class="btn btn-warning"> <i
class="fas fa-user-edit ml-2"></i>
</a></td>
<td><a th:href="@{/delete/{id}(id=${user.id})}"
class="btn btn-danger"> <i
class="fas fa-user-times ml-2"></i>
</a></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

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

Hit this URL in your local system, http://localhost:8080/


More ...

Kotlin + Spring Boot CRUD examples:

Kotlin + Spring Boot with Template Engines CRUD examples:

Build REST APIs with Kotlin + Spring Boot:

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

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