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:
Update User:
Download the source code
More related topics ...