Table of Contents
This article is the continuation article of Spring Boot with DB Migration (1 of 2).
Build Application
From now on, create Spring Boot application. Let’s create files in the follwong structure.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
demo/ `- src/ `- main/ |- kotlin/ | `- com/ | `- example/ | `- demo/ | |- domain/ | | `- task | | |- Task.kt | | `- TaskRepository.kt | |- presentation/ | | `- TaskController.kt | `- DemoApplication.kt `- resources/ |- application.properties `- templates/ `- task/ `- index.html |
Add configuration
Update application.properties
and DemoApplication
class.
Add database configuration to the application.properties
.
1 2 3 4 5 |
spring.datasource.url=jdbc:postgresql://localhost:5432/demo spring.datasource.username=developer spring.datasource.password=developer spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect |
Add @EnableJpaAuditing
annotation to DemoApplication
class. It is used for timestamp auto updating.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.example.demo import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.data.jpa.repository.config.EnableJpaAuditing @SpringBootApplication @EnableJpaAuditing class DemoApplication fun main(args: Array<String>) { runApplication<DemoApplication>(*args) } |
Entity
Create an entity, which represents the record in the database. Create the file of the class , Task
, as domain/task/Task.kt
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package com.example.demo.domain.task import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.util.* import javax.persistence.* @Entity @EntityListeners(AuditingEntityListener::class) class Task { @Id @SequenceGenerator( name = "task_id_seq", sequenceName = "task_id_seq", allocationSize = 1 ) @GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "task_id_seq" ) var id: Long? = null var name: String? = null @CreatedDate @Temporal(TemporalType.TIMESTAMP) @Column(nullable = false, updatable = false) var createdAt: Date? = null @LastModifiedDate @Temporal(TemporalType.TIMESTAMP) @Column(nullable = false, updatable = false) var updatedAt: Date? = null } |
@EntityListeners
, @CreateDate
and @LastModifiedDate
are added for auto updating.
Repository
Create the repository interface, TaskRepository
, for Task
, as domain/task/TaskRepository.kt
.
1 2 3 4 5 |
package com.example.demo.domain.task import org.springframework.data.jpa.repository.JpaRepository interface TaskRepository : JpaRepository<Task, Long> |
View template
Create the view template, which is resources/templates/task/index.html
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sample Application</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootst rap/4.1.3/css/bootstrap.min.css" crossorigin="anonymous"> </head> <body> <header class="pos-f-t mb-2"> <nav class="navbar navbar-light bg-light"> <a th:href="@{/task}" class="navbar-brand"> Task List </a> </nav> </header> <main class="container"> <div class="row" th:if="${success}"> <div class="col"> <div class="alert alert-success" role="alert" th:text="${success}"></div> </div> </div> <div class="row" th:each="task : ${taskList}"> <div class="col"> <div class="d-flex"> <div class="p-2 w-100 align-self-center" th:text="${task.name}"> </div> <div class="p-2 flex-shrink-1 align-self-center"> <form th:object="${task}" th:action="@{/task/{id}(id=${task.id})}" th:method="delete"> <button class="btn btn-outline-danger btn-sm" type="submit"> X </button> </form> </div> </div> </div> </div> <hr> <div class="row"> <div class="col"> <form th:method="post" th:action="@{/task}" th:object="${newTask}"> <div class="form-group"> <input type="text" name="name" class="form-control" th:field="*{name}"> </div> <div class="form-group"> <button class="btn btn-primary btn-block" type="submit"> Submit </button> </div> </form> </div> </div> </main> </body> </html> |
Controller
Create the controller, TaskController
, as presentation/task/TaskController.kt
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package com.example.demo.presentation.task import com.example.demo.domain.task.Task import com.example.demo.domain.task.TaskRepository import org.springframework.data.domain.Sort import org.springframework.stereotype.Controller import org.springframework.ui.ModelMap import org.springframework.web.bind.annotation.* import org.springframework.web.servlet.mvc.support.RedirectAttributes @Controller @RequestMapping("task") class TaskController( private val taskRepository: TaskRepository ) { @GetMapping fun list(modelMap: ModelMap): String { modelMap["taskList"] = taskRepository.findAll( Sort.by("id").descending() ) modelMap["newTask"] = Task() return "task/index" } @PostMapping fun create( attributes: RedirectAttributes, @ModelAttribute task: Task ): String { taskRepository.save(task) attributes.addFlashAttribute( "success", "The Task was successfully created." ) return "redirect:/task" } @DeleteMapping("{id}") fun delete( attributes: RedirectAttributes, @PathVariable id: Long ): String { taskRepository.deleteById(id) attributes.addFlashAttribute( "success", "The Task was successfully deleted." ) return "redirect:/task" } } |
Run the application
Execute ./gradlew bootrun
, and after awhile, you can see the page on the browser. Please access to http://localhost:8080/task
. And you can add and remove tasks.
The code of this demo project is on GitHub.