Kotlin + Spring + Groovy Templates CRUD Example

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

The Groovy markup template engine provides an innovative templating system based on the builder syntax.
 
It offers various key features: 
  • hierarchical (builder) syntax to generate XML-like contents (in particular, HTML5) 
  • compilation of templates to bytecode for fast rendering
  • internationalization 
  • layout mechanism for sharing structural patterns 
  • optional type checking
and more! 

The following technologies stack being used:

  • Spring Boot 2.5.5
  • Spring MVC 5.3.10
  • Gradle
  • Kotlin
  • Groovy templates 2.5.5
  • 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-groovy-templates")
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 User Entity

package com.knf.dev.demo.kotlinspringbootcrudexample.model

import javax.persistence.*

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

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

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

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



Create CRUD Repository

package com.knf.dev.demo.kotlinspringbootcrudexample.repository

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

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


Create the User Service

package com.knf.dev.demo.kotlinspringbootcrudexample.service

import com.knf.dev.demo.kotlinspringbootcrudexample.model.User
import org.springframework.beans.factory.annotation.Autowired
import com.knf.dev.demo.kotlinspringbootcrudexample.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.first_name = entity.first_name
newEntity.last_name = entity.last_name
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

package com.knf.dev.demo.kotlinspringbootcrudexample.controller

import com.knf.dev.demo.kotlinspringbootcrudexample.model.User
import org.springframework.beans.factory.annotation.Autowired
import com.knf.dev.demo.kotlinspringbootcrudexample.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("create", true)
model.addAttribute("actionUrl", "/create")
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("create", false)
model.addAttribute(
"actionUrl",
"/update/" + if (user == null) 0 else user.id
)
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 Groovy templates

home.tpl

yieldUnescaped '<!DOCTYPE html>'
html(lang: 'en') {
head {
meta('http-equiv': '"Content-Type" content="text/html; ' +
'charset=utf-8"')
title("Groovy example")
link(rel: "stylesheet", type: "text/css",
href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css")

}
body {

div(class: 'container') {
h2("User CRUD operation with Groovy Template")
div {
nobr {
a(class: 'btn btn-primary', href: "/create", "Add User")
}
}
br()
br()
div {
table(class: 'table') {
tr {
th("Id")
th("First Name")
th("Last Name")
th("Email")
th("")
th("")
}
users.each { user ->
tr {
td("$user.id")
td("$user.first_name")
td("$user.last_name")
td("$user.email")
td {
a(class: 'btn btn-warning',
href: "/update/$user.id", "Edit")
}
td {
a(class: 'btn btn-danger',
href: "/delete/$user.id", "Delete")
}
}
}
}
}

}
}
}



create-update.tpl

yieldUnescaped '<!DOCTYPE html>'
html(lang: 'en') {
head {
meta('http-equiv': '"Content-Type" content="text/html; ' +
'charset=utf-8"')
title("Groovy example")
link(rel: "stylesheet", type: "text/css",
href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css")
}
body {
div(class: 'container') {
if (create) {
h1("Create a User:")
} else {
h1("Edit User")
}
a(class: 'btn btn-primary', href: "/", "Back to User List")
br()
br()
form(id: "editForm", action: "$actionUrl", method: "POST") {
table(class: 'table') {
if (!create) {
tr {
td("Id")
td(":")
td(user.id ?: '')
}
}
tr {
td("First Name")
td(":")
td {
input(name: 'first_name', type: 'text',
value: user.last_name ?: '')
}
}
tr {
td("Last Name")
td(":")
td {
input(name: 'last_name', type: 'text',
value: user.last_name ?: '')
}
}
tr {
td("Email")
td(":")
td {
input(name: 'email', type: 'text',
value: user.email ?: '')
}
}
}
br()
if (create) {
input(class: 'btn btn-success', type: 'submit',
value: 'Create')
} else {
input(class: 'btn btn-success', type: 'submit',
value: 'Update')
}
}
}
}
}



Spring Boot Main Driver

package com.knf.dev.demo.kotlinspringbootcrudexample

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

@SpringBootApplication
class KotlinspringbootcrudexampleApplication

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


Run

Start Spring Boot: gradle bootRun

Access the URL in  the browser: http://localhost:8080/

Create User:

List all Users:

Update User:

Download the source code



Comments

Popular posts from this blog

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

Java, Spring Boot Mini Project - Library Management System - Download

Java - Blowfish Encryption and decryption Example

Java - DES Encryption and Decryption example

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete

ReactJS - Bootstrap - Buttons

Spring Boot 3 + Spring Security 6 + Thymeleaf - Registration and Login Example

File Upload, Download, And Delete - Azure Blob Storage + Spring Boot Example

Top 5 Java ORM tools - 2024

Java - How to Count the Number of Occurrences of Substring in a String