[JAVA] The first WEB application with Spring Boot-Making a Pomodoro timer-

Overview

This article is a record of the first attempt to create a web application from scratch. I have not been able to explain the technology because there are some parts that I do not understand, but since I have posted the flow from design to implementation and the source code, I hope that it will be a little reference for other beginners.

Completed

A web application that records the start time of work, displays an alert after 50 minutes, and records the end time of work.

image.png

environment

Deliverables

Project tree


.
├── src
│   ├── main
│   │   ├── java
│   │   │   └── jp
│   │   │       └── co
│   │   │           └── anyplus
│   │   │               ├── Entity
│   │   │               │   └── TasksEntity.java
│   │   │               ├── Repository
│   │   │               │   └── TasksRepository.java
│   │   │               ├── ServletInitializer.java
│   │   │               ├── TaskTrackerApplication.java
│   │   │               ├── controller
│   │   │               │   └── IndexController.java
│   │   │               ├── form
│   │   │               │   └── TaskForm.java
│   │   │               └── service
│   │   │                   ├── TaskCancelService.java
│   │   │                   └── TaskRegisterService.java
│   │   ├── resources
│   │   │   ├── application.properties
│   │   │   ├── static
│   │   │   │   ├── css
│   │   │   │   │   ├── bootstrap-grid.css
│   │   │   │   │   ├── bootstrap-grid.css.map
│   │   │   │   │   ├── bootstrap-grid.min.css
│   │   │   │   │   ├── bootstrap-grid.min.css.map
│   │   │   │   │   ├── bootstrap-reboot.css
│   │   │   │   │   ├── bootstrap-reboot.css.map
│   │   │   │   │   ├── bootstrap-reboot.min.css
│   │   │   │   │   ├── bootstrap-reboot.min.css.map
│   │   │   │   │   ├── bootstrap.css
│   │   │   │   │   ├── bootstrap.css.map
│   │   │   │   │   ├── bootstrap.min.css
│   │   │   │   │   ├── bootstrap.min.css.map
│   │   │   │   │   └── jQuery.countdownTimer.css
│   │   │   │   ├── images
│   │   │   │   └── js
│   │   │   │       ├── bootstrap.bundle.js
│   │   │   │       ├── bootstrap.bundle.js.map
│   │   │   │       ├── bootstrap.bundle.min.js
│   │   │   │       ├── bootstrap.bundle.min.js.map
│   │   │   │       ├── bootstrap.js
│   │   │   │       ├── bootstrap.js.map
│   │   │   │       ├── bootstrap.min.js
│   │   │   │       ├── bootstrap.min.js.map
│   │   │   │       ├── import.js
│   │   │   │       ├── jQuery.countdownTimer.js
│   │   │   │       ├── jQuery.countdownTimer.min.js
│   │   │   │       ├── jQuery.countdownTimer.min.js.map
│   │   │   │       ├── jquery-3.4.1.min.js
│   │   │   │       ├── localisation
│   │   │   │       └── view-index.js
│   │   │   └── templates
│   │   │       └── index.html

design

I made the function as simple as possible, and made it on the premise that it can be made in one day for the time being.

Functional design

// The result display has been postponed this time. I want to implement it in the future.

The sequence diagram is as follows.

image.png

Table design

Create only one table because you only need to store the tasks you have performed.

** Task table **

column Description
task_id Sequence value
task_name Task name
task_category Category of work.
task_starttime Work start time
task_endtime Work end time
userid USER ID. Because there is no user functionsystemAssuming that only can be entered

DB We prepared the DB for this WEB application and made the table as follows.

DDL


CREATE TABLE public.tasks (
	task_id bigserial NOT NULL,
	task_name varchar(200) NULL,
	task_category varchar(200) NULL,
	task_starttime timestamp NULL,
	task_endtime timestamp NULL,
	userid varchar(20) NULL
);

Creating a project

Create a project. This time, I made it from the Spring Starter project as follows.

image.png

image.png

The Maven repository set above is as follows.

--SpringBootDevTool: A convenient development tool that automatically compiles and restarts when Java source changes are triggered. --SpringDataJpa: A convenient library for DB operations. It does automatic type conversion between Java <-> DB. --PostgreSQL Driver: Driver for accessing PostgreSQL --Thymeleaf: Template engine. Variables described by attribute values are automatically replaced with values --SpringWebStarter: A definition set that includes all the settings and library dependencies required for a web application.

After creating the project, right-click on the project> Execute> Maven Install to install the repository file defined in pom.xml.

In this procedure, pom.xml is automatically generated in Spring Starter project, When changing the version or adding the library manually, it seems better to edit pom.xml directly and perform Maven Install or Maven Clean and Install. You can find the Maven repository from this site.

Next, open the application.properties file and set the DB connection settings.

application.properties


spring.datasource.url=jdbc:postgresql://localhost:5432/[Database name]
spring.datasource.username=[Database user]
spring.datasource.password=[Database password]
spring.datasource.driver-class-name=org.postgresql.Driver

Run it to see if the project is ready without any problems. (Right click on the project> Run> Spring Boot application)

Confirm that the following message is output to the console log and proceed to the next.

o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s):-(Abbreviation)-
jp.co.anyplus.TaskTrackerApplication     : Started [Application name] in [Numerical value] seconds (JVM running for [Numerical value])

Screen implementation

Library

First, put in the required libraries. There was jQuery as an extension of JavaScript, Bootstrap for layout, and Countdown Timer for timer, so I used this.

CountdownTimer (https://github.com/harshen/jQuery-countdownTimer/) A jQuery library that has all the functions of date and time countup and countdown. It was perfect for this app because it has all the functions of layout specification and Start / Stop.

html I made only one screen this time.

The layout is decided by Bootstrap. For the timer, the display area is determined by <span id =" timerView ">, and the display location is specified for the parts of the Coundown Timer. The default CSS of CoundownTimer is a little unsatisfactory, so I am editing it, but I will omit it here.

index.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/css/jQuery.countdownTimer.css" />

<title>TaskTracker</title>
</head>
<body>
    <main class="container">
        <h1>Task Tracker</h1>
        <p>Record the task.</p>
        <div class="container text-center">
            <div id="taskMainContainer" class="jumbotron col-md-8 mx-auto">
                <form id="TaskForm" th:object="${taskForm}">
                    <div class="row">
                        <div class="col display-4"><span id="timerView">50:00</span></div>
                    </div>
                    <div class="row mt-3 justify-content-md-center">
                        <div class="col-md-4 "><button id="startButton" type="button" class="btn btn-block btn-success" disabled>Start</button></div>
                        <div class="col-md-4 "><button id="stopButton" type="button" class="btn btn-block btn-danger" disabled>Stop</button></div>
                    </div>
                    <div class="row mt-3 justify-content-md-center">
                        <div class="col-md-4 p-1"><input th:field="*{category}" type="text" class="form-control" placeholder="category"></div>
                        <div class="col-md-8 p-1"><input th:field="*{taskName}" type="text" class="form-control" placeholder="task"></div>
                        <div><input th:field="*{taskId}" type="text" class="form-control" hidden></div>
                    </div>
                </form>
            </div>
        </div>
    </main>
  
    <script src="/js/import.js"></script>
    <script src="/js/view-index.js"></script>
  </body>
</html>

JavaScript I made import.js for importing the library and view-index.js for describing screen processing.

import.js


/**
 * import
 **/
document.write('<script type="text/javascript" src="/js/jquery-3.4.1.min.js"></script>');
document.write('<script type="text/javascript" src="/js/bootstrap.bundle.min.js"></script>');

view-index.js


/*--------------------------------
 * index.JS for html
 --------------------------------*/
document.write('<script type="text/javascript" src="/js/jQuery.countdownTimer.js"></script>');

$(function(){
    $("#timerView").countdowntimer({
        minutes :50,
        seconds :00,
        displayFormat : "MS",
        size : "xl",
        timeUp : taskFinish
    });
    $("#timerView").countdowntimer("stop", "stop");
    stopbtnOn();
});

/*--------------------------------
 *Start task
 --------------------------------*/
 $('#startButton').on('click', taskStart);
 function taskStart(){
    startbtnOn();
    $("#timerView").countdowntimer("stop", "start");

    var form = $('#TaskForm').serializeArray();
    var formdata = {};
    jQuery.each(form, function(i, e) {
    	formdata[e.name] = e.value;
    });
    
    $.ajax({
        url:'/',
        type:'POST',
        contentType : 'application/json; charset=utf-8',
        data: JSON.stringify(formdata)
    }).done( (data) => {
        console.log("success");
        console.log(data);
        $('#taskId').val(data);
    }).fail( (data) => {
        console.log("fail");
        console.log(data);
    });
}

/*--------------------------------
 *Stop task
 --------------------------------*/
$('#stopButton').on('click', taskStop);
function taskStop(){
    stopbtnOn();
    $("#timerView").countdowntimer("stop", "stop");

    var form = $('#TaskForm').serializeArray();
    var formdata = {};
    jQuery.each(form, function(i, e) {
    	formdata[e.name] = e.value;
    });
    
    $.ajax({
        url:'/stop',
        type:'POST',
        contentType : 'application/json; charset=utf-8',
        data: JSON.stringify(formdata)
    }).done( (data) => {
        console.log("success");
        console.log(data);
    }).fail( (data) => {
        console.log("fail");
        console.log(data);
    });
}

/*--------------------------------
 *Task completed
 --------------------------------*/
function taskFinish(){
    setTimeout(() => {
        taskStop();
        alert("Finish!!");
        $("#timerView").countdowntimer("stop", "stop");
        stopbtnOn();
    }, 1000);
}

/*--------------------------------
 *Button control: When the start button is ON
 --------------------------------*/
function startbtnOn(){
    $('#startButton').prop("disabled", true);
    $('#stopButton').prop("disabled", false);
}

/*--------------------------------
 *Button control: When the stop button is ON
 --------------------------------*/
function stopbtnOn(){
    $('#startButton').prop("disabled", false);
    $('#stopButton').prop("disabled", true);
}

Server-side implementation

Model layer class

First, create the TasksEntity class for storing table data and the TasksRepository class for operating the table created in the DB.

TasksEntity.java


package jp.co.anyplus.Entity;

import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="tasks")
public class TasksEntity {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name="task_id")
	private Long taskId;
	@Column(name="task_name")
	private String taskName;
	@Column(name="task_category")
	private String taskCategory;
	@Column(name="task_starttime")
	private Timestamp taskStartTime;
	@Column(name="task_endtime")
	private Timestamp taskEndTime;
	@Column(name="userid")
	private String userid;
	
	public Long getTaskId() {
		return taskId;
	}
	public void setTaskId(Long taskId) {
		this.taskId = taskId;
	}
	public String getTaskName() {
		return taskName;
	}
	public void setTaskName(String taskName) {
		this.taskName = taskName;
	}
	public String getTaskCategory() {
		return taskCategory;
	}
	public void setTaskCategory(String taskCategory) {
		this.taskCategory = taskCategory;
	}
	public Timestamp getTaskStartTime() {
		return taskStartTime;
	}
	public void setTaskStartTime(Timestamp taskStartTime) {
		this.taskStartTime = taskStartTime;
	}
	public Timestamp getTaskEndTime() {
		return taskEndTime;
	}
	public void setTaskEndTime(Timestamp taskEndTime) {
		this.taskEndTime = taskEndTime;
	}
	public String getUserid() {
		return userid;
	}
	public void setUserid(String userid) {
		this.userid = userid;
	}
}

TasksRepository.java


package jp.co.anyplus.Repository;

import java.sql.Timestamp;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import jp.co.anyplus.Entity.TasksEntity;

@Repository
public interface TasksRepository extends JpaRepository<TasksEntity, Long> {

	@Query(value="select current_timestamp", nativeQuery = true)
	public Timestamp getCurrentTime();
	
	@Query(value="select * from tasks where task_id = :taskId"
			, nativeQuery = true)
	public TasksEntity getByTaskId(@Param("taskId")Long taskId);
}

Since JpaRepository inherited by TasksRepository.java can use the function of Spring Data JPA (reference article), It seems that the content of this query could be automatically generated from the method name without writing SQL.

Controller layer and business related classes

Create around the Controller layer to flow from View to Model. IndexController.java of Cotroller class, TaskRegisterService.java and TaskCancelService.java of Service class, Screen <-> We have prepared a total of 4 classes of TaskForm.java for passing task information between servers.

IndexController.java


//Package and import statements omitted

@Controller
public class IndexController {

	@Autowired
	TaskRegisterService registerService;
	@Autowired
	TaskCancelService cancelService;

	/**
	 *Initial display processing
	 * 
	 * @param mav ModelAndView
	 * @return Initial display information
	 */
	@GetMapping("/")
	public ModelAndView init(ModelAndView mav) {
		TaskForm taskForm = new TaskForm();
		
		mav.addObject("taskForm", taskForm);
		mav.setViewName("index.html");
		return mav;
	}

	/**
	 *Task registration process
	 * 
	 * @param model Model
	 * @param taskForm Task information Form
	 * @return Newly assigned task ID
	 */
	@PostMapping("/")
	@ResponseBody
	public String taskRegister(Model model, @RequestBody TaskForm taskForm) {

		try {

			TasksEntity regTask = registerService.registerTask(taskForm);
			
			return regTask.getTaskId().toString();

		} catch (Exception e) {
			e.printStackTrace();
			return "";
		}
	}
	
	/**
	 *Task end processing
	 * 
	 * @param model Model
	 * @param taskForm Task information Form
	 * @return processing message
	 */
	@PostMapping("/stop")
	@ResponseBody
	public String taskEnd(Model model, @RequestBody TaskForm taskForm) {
		
		try {
			cancelService.endTask(taskForm);
		} catch(RuntimeException e) {
			e.printStackTrace();
			return "error";
		}
		
		return "task canceling success";
	}

}

I haven't come to understand the @ResponseBody, @ PostMapping / @GetMapping, and Model classes used to communicate between the screen and the server. If you understand, I would like to review the source and make a Qiita article.

TaskRegisterService.java


//Package and import statements omitted

@Service
public class TaskRegisterService {

	@Autowired
	TasksRepository tasksRep;

	@Transactional(rollbackOn = Exception.class)
	public TasksEntity registerTask(TaskForm taskForm) throws RuntimeException {
		Timestamp currenttime = tasksRep.getCurrentTime();

		TasksEntity taskEntity = new TasksEntity();
		taskEntity.setTaskName(taskForm.getTaskName());
		taskEntity.setTaskCategory(taskForm.getCategory());
		taskEntity.setTaskStartTime(currenttime);
		taskEntity.setUserid("system");

		try {
			TasksEntity regTask = tasksRep.saveAndFlush(taskEntity);
			return regTask;
		} catch (RuntimeException e) {
			//log: Registration failed.
			throw e;
		}
	}

}

TaskCancelService.java


//Package and import statements omitted

@Service
public class TaskCancelService {

	@Autowired
	TasksRepository tasksRep;

	@Transactional(rollbackOn = Exception.class)
	public void endTask(TaskForm taskForm) throws RuntimeException {
		Timestamp currenttime = tasksRep.getCurrentTime();

		TasksEntity taskEntity = tasksRep.getByTaskId(Long.parseLong(taskForm.getTaskId()));
		
		taskEntity.setTaskId(Long.parseLong(taskForm.getTaskId()));
		taskEntity.setTaskEndTime(currenttime);

		try {
			tasksRep.saveAndFlush(taskEntity);
		} catch (RuntimeException e) {
			//log: Update failed.
			throw e;
		}
	}

}

TaskForm.java


//Package and import statements omitted

@SuppressWarnings("serial")
public class TaskForm implements java.io.Serializable {

	private String taskId;
	private String category;
	private String taskName;
	
	public String getTaskId() {
		return taskId;
	}
	public void setTaskId(String taskId) {
		this.taskId = taskId;
	}
	public String getCategory() {
		return category;
	}
	public void setCategory(String category) {
		this.category = category;
	}
	public String getTaskName() {
		return taskName;
	}
	public void setTaskName(String taskName) {
		this.taskName = taskName;
	}
	
}

Recommended Posts

The first WEB application with Spring Boot-Making a Pomodoro timer-
Build a web application with Javalin
Let's make a book management web application with Spring Boot part1
Let's make a book management web application with Spring Boot part3
Let's make a book management web application with Spring Boot part2
Create a simple web application with Dropwizard
Start web application development with Spring Boot
Run WEB application with Spring Boot + Thymeleaf
Create a web api server with spring boot
Build a WEB system with Spring + Doma + H2DB
Sign in to a Spring Boot web application on the Microsoft ID platform
I tried to clone a web application full of bugs with Spring Boot
Build a WEB system with Spring + Doma + H2DB + Thymeleaf
Roughly the flow of web application development with Rails.
Build a WEB system with Spring + Doma + H2DB Part 2
A story that stumbled when deploying a web application created with Spring Boot to EC2
A story packed with the basics of Spring Boot (solved)
[Spring Boot] Web application creation
Check the operation of two roles with a chat application
Create a Hello World web app with Spring framework + Jetty
Until you create a Web application with Servlet / JSP (Part 1)
Try developing a containerized Java web application with Eclipse + Codewind
Web application built with docker (1)
Modeling a Digimon with DDD for the first time Part 1
AWS Elastic Beanstalk # 1 with Java starting from scratch-Building a Java web application environment using the EB CLI-
Implement a simple Web REST API server with Spring Boot + MySQL
Create a simple web server with the Java standard library com.sun.net.httpserver
The story of the first Rails app refactored with a self-made helper
Read the file under the classpath as a character string with spring
[Probably the easiest] WEB application development with Apache Tomcat + Java Servlet
What is the difference between a web server and an application server?
A story that struggled with the introduction of Web Apple Pay
Deploy a Docker application with Greengrass
Inquiry application creation with Spring Boot
Creating a timer app with a muddy
Memo after the first Spring project-MVC-
Web application creation with Nodejs with Docker
Spring AOP for the first time
Memo after the first Spring project-Database-
[Spring Boot] Precautions when developing a web application with Spring Boot and placing war on an independent Tomcat server
Create a Spring Boot app development project with the cURL + tar command
How to check before sending a message to the server with Spring Integration
Spring Boot2 Web application development with Visual Studio Code SQL Server connection
Spring5 MVC web application development with Visual Studio Code SQL Server connection
Creating a java web application development environment with docker for mac part1
Java beginner tried to make a simple web application using Spring Boot
Spring Boot2 Web application development with Visual Studio Code Hello World creation
Automatically deploy a web application developed in Java using Jenkins [Spring Boot application]
Deploy the WEB application by Spring Boot to Tomcat server as WAR
Spring5 MVC Web application development with Visual Studio Code Maven template creation
[Java] Deploy a web application created with Eclipse + Maven + Ontology on Heroku
Create a java web application development environment with docker for mac part2
Send a request to the backend after authenticating with Spring Cloud Gateway
Processing at application startup with Spring Boot
Create a jar file with the command
Memo after the first Spring project-What is Spring-
Launch Nginx + Spring Boot application with docker-compose
Run a DMN with the Camunda DMN Engine
CI / CD Spring application with CircleCI (Heroku)
Let's make a calculator application with Java ~ Create a display area in the window
Spring5 MVC Web application development with Visual Studio Code Spring Security usage 2/3 [Page creation 1/2]