Kotlin + Spring Boot + FreeMarker + JPA - CRUD example

Hello everyone, Today we will learn how to develop a Spring Boot CRUD web application, using Kotlin, Spring Boot, FreeMarker template, H2DB, and JPA.


Following technologies stack being used:
  • Spring Boot 2.5.5
  • Spring MVC 5.3.10
  • Gradle
  • Kotlin
  • FreeMarker 2.3.31
  • H2DB 
  • Bootstrap

Project Structure:


Project Dependency(build.gradle.kts)

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

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

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-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-freemarker")
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")
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()
}

Configure the Entity class

package com.knf.dev.demo.kotlinspringjpafreemarkercrud.model

import javax.persistence.*

@Entity
@Table(name = "user")
class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null

@Column(name = "first_name")
var firstName: String? = null

@Column(name = "last_name")
var lastName: String? = null

@Column(name = "email", nullable = false, length = 200)
var email: String? = null

constructor() : super() {}
constructor(firstName: String?, lastName: String?, email: String?) : super() {
this.firstName = firstName
this.lastName = lastName
this.email = email
}

override fun toString(): String {
return ("User [id=" + id + ", firstName=" + firstName + ", lastName="
+ lastName + ", email=" + email + "]")
}
}

Create CRUD Repository

package com.knf.dev.demo.kotlinspringjpafreemarkercrud.repository

import com.knf.dev.demo.kotlinspringjpafreemarkercrud.model.User
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository

@Repository
interface UserRepository : CrudRepository<User?, Long?>

Create the Service class

package com.knf.dev.demo.kotlinspringjpafreemarkercrud.service

import com.knf.dev.demo.kotlinspringjpafreemarkercrud.model.User
import org.springframework.beans.factory.annotation.Autowired
import com.knf.dev.demo.kotlinspringjpafreemarkercrud.repository.UserRepository
import org.springframework.stereotype.Service
import java.util.ArrayList
import kotlin.Throws
import javax.persistence.EntityNotFoundException

@Service
class UserService {
@Autowired
var repository: UserRepository? = null
val allusers: List<User?>
get() {
val result = repository!!.findAll() as List<User?>
return if (result.size > 0) {
result
} else {
ArrayList()
}
}

@Throws(EntityNotFoundException::class)
fun getUserById(id: Long): User {
val user = repository!!.findById(id)
return if (user.isPresent) {
user.get()
} else {
throw EntityNotFoundException("No user record exist for given id")
}
}

fun createOrUpdateUser(entity: User): User {
var entity = entity
return if (entity.id == null) {
entity = repository!!.save(entity)
entity
} else {
val user = repository!!.findById(
entity.id!!
)
if (user.isPresent) {
var newEntity = user.get()
newEntity.email = entity.email
newEntity.firstName = entity.firstName
newEntity.lastName = entity.lastName
newEntity = repository!!.save(newEntity)
newEntity
} else {
entity = repository!!.save(entity)
entity
}
}
}

@Throws(EntityNotFoundException::class)
fun deleteUserById(id: Long) {
val user = repository!!.findById(id)
if (user.isPresent) {
repository!!.deleteById(id)
} else {
throw EntityNotFoundException("No user record exist for given id")
}
}
}

Create the Web Controller class

package com.knf.dev.demo.kotlinspringjpafreemarkercrud.controller

import com.knf.dev.demo.kotlinspringjpafreemarkercrud.model.User
import org.springframework.beans.factory.annotation.Autowired
import com.knf.dev.demo.kotlinspringjpafreemarkercrud.service.UserService
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PathVariable
import kotlin.Throws
import javax.persistence.EntityNotFoundException

@Controller
class UserController {
@Autowired
private val userService: UserService? = null
@GetMapping("/")
fun getAllUserView(model: Model): String {
val users = userService!!.allusers
model.addAttribute("users", users)
return "home"
}

@GetMapping("/create")
fun createUserView(model: Model): String {
val user = User()
model.addAttribute("user", user)
model.addAttribute("isUpdate", false)
return "create-update"
}

@PostMapping("/update/{id}")
fun createUser(
@ModelAttribute("user") user: User,
@PathVariable("id") id: Long?
): String {
user.id = id
userService!!.createOrUpdateUser(user)
return "redirect:/"
}

@GetMapping("/update/{id}")
@Throws(EntityNotFoundException::class)
fun updateUser(
model: Model,
@PathVariable("id") id: Long?
): String {
val user = userService!!.getUserById(id!!)
model.addAttribute("user", user)
model.addAttribute("isUpdate", true)
return "create-update"
}

@PostMapping("/create")
fun createUser(@ModelAttribute("user") user: User?): String {
userService!!.createOrUpdateUser(user!!)
return "redirect:/"
}

@GetMapping("/delete/{id}")
@Throws(EntityNotFoundException::class)
fun deleteUser(@PathVariable("id") id: Long?): String {
userService!!.deleteUserById(id!!)
return "redirect:/"
}
}

Creating FreeMarker templates

home.ftlh

<!doctype html>
<head>
<title>Freemarker Example</title>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js">
</script>
</head>
<body>
<div class="container">
<h2>User CRUD operation with Freemarker Template</h2>
<a href="/create" class="btn btn-primary" role="button">Create New User</a>
<table class="table">
<thead >
<tr>
<th scope="col">Id</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Email</th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<#list users as user>
<tr>
<th scope="row">${user.id}</th>
<td>${user.firstName}</td>
<td>${user.lastName}</td>
<td>${user.email}</td>
<td><a href="/update/${user.id}" class="btn btn-warning" role="button">
                     Update</a></td>
<td><a href="/delete/${user.id}" class="btn btn-danger" role="button">
                     Delete</a></td>
</tr>
</#list>
</tbody>
</table>
</div>
</body>
</html>

create-update.ftlh

<!doctype html>
<head>
<title>Freemarker Example</title>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js">
</script>
</head>
<body>
<div class="container">
<h2>
<#if !isUpdate>Create</#if>
<#if isUpdate>Update</#if>
User
</h2>
<div>
<form action="<#if isUpdate>/update/${user.id}</#if><#if !isUpdate>/create</#if>"
method="post" name="user">
<table class="table">
<tbody>
<thead>
<tr>
<th>Field</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<#if isUpdate>
<tr>
<th>ID</th>
<td>
<div name="id">${user.id}</div>
</td>
</tr>
</#if>
<tr>
<th>First Name</th>
<td><input name="firstName" type="text"
value="<#if user.firstName??>${user.firstName}</#if>"/>
                                                    </td>
</tr>
<tr>
<th>Last Name</th>
<td><input name="lastName" type="text"
value="<#if user.lastName??>${user.lastName}</#if>"/></td>
</tr>
<tr>
<th>Email</th>
<td><input name="email" type="text"
value="<#if user.email??>${user.email}</#if>"/></td>
</tr>
</tbody>
</table>
<button class="btn btn-primary" type="submit">Save</button>
</form>
</div>
</div>
</body>
</html>

Spring Boot Main Class

package com.knf.dev.demo.kotlinspringjpafreemarkercrud

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

@SpringBootApplication
class KotlinspringjpafreemarkercrudApplication

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

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