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.Userimport org.springframework.data.jpa.repository.JpaRepositoryimport org.springframework.stereotype.Repository
@Repositoryinterface 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.Userimport com.knf.dev.repository.UserRepositoryimport org.springframework.http.HttpStatusimport org.springframework.http.MediaTypeimport org.springframework.http.ResponseEntityimport org.springframework.stereotype.Controllerimport org.springframework.web.bind.annotation.*import org.springframework.ui.Modelimport org.springframework.web.bind.annotation.GetMappingimport java.util.*import org.springframework.web.bind.annotation.PathVariable
@Controllerclass 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.SpringBootApplicationimport org.springframework.boot.runApplication
@SpringBootApplicationclass 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/
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 ...
Kotlin + Spring Boot CRUD examples:
Kotlin + Spring Boot with Template Engines CRUD examples:
Build REST APIs with Kotlin + Spring Boot:
- Build REST API's with Kotlin, Spring, and MongoDB
- Build REST API's with Kotlin, Spring, Spring Data JPA, and RDBMS
- Build REST CRUD APIs with Kotlin, Spring Boot and MyBatis
Kotlin + Spring Boot + Spring Security:
- Kotlin + Spring Boot + Keycloak - Securing REST APIs
- Kotlin - Spring Security -Google OAuth2 Login - Example
- Kotlin - Spring Security -GitHub OAuth2 Login - Example
- Kotlin Spring Security JWT Authorization and Authentication
- Registration and Login with Kotlin + Spring Security + Thymeleaf
Miscellaneous:
- Vaadin + Kotlin CRUD example
- Kotlin + Spring Webflux File Upload (Single/Multiple)- REST API Example
- Kotlin - Spring Boot - WebFlux - Video Streaming example
- Kotlin + Spring Boot + OpenCSV Export Data to CSV Example
- Kotlin + Spring Boot + Apache Commons Export Data to CSV Example
- Kotlin + Spring Boot + iText PDF - Export data to PDF example
More...
- WebSocket - Kotlin - Spring boot web application example
- Kotlin: RSA + AES a double layer security system
- Kotlin hashing - Using MD5, SHA-1, SHA-256, SHA-384, SHA-512, and PBKDF2
- Kotlin- AES, RSA, 3DES Encryption and Decryption with example
- Iterate over the Map, List, and Set in Kotlin! How?
- Kotlin Programming in Visual Studio Code IDE [Ubuntu]