Kotlin + Spring Boot + Thymeleaf + 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.GitHub repository link is provided at the end of this tutorial. You can download the source code.


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 3
  • Java 8

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>


Creating the Entity[User.kt]

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
)


Creating the Repository[UserRepository.kt]

@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>


Creating the controller End-points[UserController.kt]

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)
}


application.properties

spring.h2.console.enabled=true
spring.h2.console.path=/h2_console
spring.datasource.url=jdbc:h2:file:~/h2/knf-test
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql=true

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>

Run 

Github repository download link is provided at the end of this tutorial

Local Setup:

Step 1: Download or clone the source code to a local machine.

Spring Boot - Backend Project Setup

Step 2mvn clean install

Step 3: Run the Spring Boot application
mvn spring-boot:run

 Download source code

git clone: 


More Kotlin Examples,

Popular posts from this blog

Spring boot video streaming example-HTML5

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

Spring Boot + Mockito simple application with 100% code coverage

Spring Boot + OpenCSV Export Data to CSV Example

Custom Exception Handling in Quarkus REST API

DataTable-Pagination example with Spring boot, jQuery and ajax

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Node JS mini projects with source code - free download

Spring boot web project free download:User Registration System

Java - Blowfish Encryption and decryption Example