In this post you’ll learn how to build a TaskApp using Spring Boot, a mySQL Database + JPA, and Thymeleaf + Bootstrap as Frontend. Learn how to create, read, update and delete a domain model (Tasks) by building a RESTful CRUD API. You will also learn how to deploy your App to Heroku and how to configure your App to start with preloaded data.
Find a working version of the finished App on Heroku(it may take a while to load) and the Source Code here. Feel free to use the code on Github for practice. The Frontend was designed by Bootstrap and can be found here. Provided under MIT License.

Development Environment:
IDE: IntelliJ, Eclipse
Install: MySQL Workbench (How to install the mySQL Workbench)
Deployment: Get a free Heroku Account
Java: Install Java on your computer and follow the installation instructions
Version Control: Install Git on your computer.
Import Project to your IDE:
Start by downloading and unpacking the Starter Project from GitHub and import it into an IDE of your choice. This post will feature IntelliJ. (1) Click on Import Project, (2) Select the folder, (3)Click next until finish. Wait until IntelliJ opens and finishes processing.

Folder Structure: Left – starting code, Right – finished structure for reference

pom.xml: SQL, JPA, Thymeleaf, Web and PostgreSQL for Heroku Dependencies:
We will store our data in an SQL Database and later use PostgreSQL to deploy the app to Heroku. PostgreSQL is free on Heroku and will be fine for our purposes. Thymeleaf is a Java Template Engine which we’ll use to code our HTML Frontend.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>taskapp</artifactId> <version>0.0.1-SNAPSHOT</version> <name>taskapp</name> <description>TaskApp with Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- for Heroku deploy--> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>2.22.0</version> </plugin> </plugins> </build> </project>
application.properties:
Connects our app to a Database “taskapp_db”. The default port is 8080. With the current cofigurations the app will insert the data from data.sql(below) and create a new DB after every restart of the app (it will drop the old DB if it exists).
spring.datasource.url=jdbc:mysql://localhost/taskapp_db?useSSL=false #place your password and username from your MySQL Workbench spring.datasource.username=root spring.datasource.password=root spring.jpa.show-sql=true #init data after every restart spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.hibernate.ddl-auto=create spring.datasource.initialization-mode=always ## Hibernate Logging logging.level.org.hibernate.SQL= DEBUG
INSERT INTO tasks (task_id, content, status, title) VALUES ( 1000, '(1)Generate a new project with Spring Initializr using Thymeleaf, MySQl, Web, and JPA', 'CLOSED', 'Setup a new project'), ( 1001, '(2)Create Folders for your model, repositories, controllers and services', 'REOPENED', 'Folder Structure'), ( 1002, '(3)Write a Task POJO with the following properties: title, status of type Status(Enum), content', 'OPEN', 'Task Entity'), ( 1003, '(4)Write an Enum Class for Status containing OPEN, CLOSED, REOPENED', 'OPEN', 'Status Enum'), ( 1004, '(5)Create a Service Interface and its Implementation', 'OPEN', 'Service Layer'), ( 1005, '(6)Complete the Application.properties file and create a new schema in your sql workbench', 'CLOSED', 'Databasa and Application Properties'), ( 1006, '(7)Write a Crud Repository for your Task entity', 'OPEN', 'Repository'), ( 1007, '(8)Write a TaskController', 'REOPENED', 'Controller');
To run the App:
(1) create a new Schema “taskapp_db” in your mySQL Workbench
(2) change password and username in application.properties file
(3) open the data.sql file. You will be asked to “Configure data source“. Create a new data source as described in the picture below. Enter your password and username from your mySQL Workbench and the name of the Databse (taskapp_db). Be sure to test the connection before closing the window.
(4) run the app by typing “mvn spring-boot:run” in the terminal
(5)open in browser http://localhost:8080 . You should see a blank page with “Congrats! Your App is Running!”
UMl Diagram of finished App:

Task Model:
The starting code contains a model “Task”. It has a unique Id and a title, content and status. Neither of them may be empty when sending the form information. However this post will not treat validation or exception handling.
@Entity @Table(name = "tasks") public class Task { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "task_id") private Long id; @NotEmpty private String title; @Lob @NotEmpty @Type(type = "org.hibernate.type.TextType") //heroku config private String content; //status may not be null @NotNull @Enumerated(value = EnumType.STRING) private Status status; public Task() { this.status = Status.OPEN; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } public void closeTask(){ this.status = Status.CLOSED; } public void reopenTask(){ this.status = Status.REOPENED; } }
Status:
Every Task will have a status of “OPEN”, “REOPENED” or “CLOSED”.
public enum Status { OPEN("open"), CLOSED("closed"), REOPENED("reopened"); private String typeOfStatus; Status(String typeOfStatus){ this.typeOfStatus = typeOfStatus; } public String getTypeOfStatus() { return typeOfStatus; } public static Stream<Status> stream() { return Stream.of(Status.values()); } }
Repository:
Next, create a new folder “repository” and a new interface “TaskRepository.java”. The repository to save files in the database and retrieve them.
@Repository public interface TaskRepository extends CrudRepository<Task, Long>{ }
Service Layer:
Create a new folder “service” and a new interface “TaskService.java” which will be implemented by “TaskServiceImpl.java”. The TaskService will provide the necessary CRUDfunctions to interact with the DB.
@Service public interface TaskService { /** * GET all tasks from DB * @return all tasks from Database */ Set<Task> getTasks(); /** * finds a task by its ID * @param taskId Database ID of task * @return task */ Task findById(Long taskId); /** * creates new Task and saves it in Database * @param taskDetails field values * @return new Task */ Task createTask(Task taskDetails); /** * updates Task from Database with field values in taskDetails * @param taskId Database ID of task * @param taskDetails field values * @return updated Task */ Task updateTask(Long taskId, Task taskDetails); /** * deletes Task from Database * @param taskId Database ID of task */ void deleteTask(Long taskId); /** * Sets Task.Status to CLOSED * @param taskId Database ID of task */ void closeTask(Long taskId); /** * Sets Task.Status to REOPENED * @param taskId Database ID of task */ void reopenTask(Long taskId); /** * Filters all Tasks by Status * @param status Enum: OPEN;CLOSED;REOPENED * @return Set of Tasks */ Set<Task> getTasksByStatus(Status status); }
TaskServiceImpl.Java:
@Service public class TaskServiceImpl implements TaskService{ @Autowired private TaskRepository taskRepository; @Override public Set<Task> getTasks() { Set<Task> taskSet = new HashSet<>(); taskRepository.findAll().iterator().forEachRemaining(taskSet::add); return taskSet; } @Override public Task findById(Long taskId) { Optional<Task> taskOptional = taskRepository.findById(taskId); if(!taskOptional.isPresent()){ throw new RuntimeException("Task Not Found!"); } return taskOptional.get(); } @Override public Task createTask(Task taskDetails) { Task newTask = taskRepository.save(taskDetails); return newTask; } @Override public Task updateTask(Long taskId, Task taskDetails) { Task task = findById(taskId); task.setTitle(taskDetails.getTitle()); task.setContent(taskDetails.getContent()); task.setStatus(taskDetails.getStatus()); taskRepository.save(task); return task; } @Override public void deleteTask(Long taskId) { taskRepository.delete(findById(taskId)); } @Override public void closeTask(Long taskId) { Task task = findById(taskId); task.closeTask(); taskRepository.save(task); } @Override public void reopenTask(Long taskId) { Task task = findById(taskId); task.reopenTask(); taskRepository.save(task); } @Override public Set<Task> getTasksByStatus(Status status) { Set<Task> allTasks = getTasks(); Set<Task> filteredTasks = new HashSet<>(); for(Task t : allTasks){ if(t.getStatus().equals(status)){filteredTasks.add(t);} } return filteredTasks; } }
Controller:
The following will be the controller for our Task Entity.
@Controller public class TaskController { @Autowired private TaskService taskService; /** * GET all tasks from Database * @return template view for all tasks */ @RequestMapping(value = {"/tasks", "/"}, method=RequestMethod.GET) public String dashboard(Model model) { //display all Tasks Set<Task> tasks = taskService.getTasks(); model.addAttribute("tasks", tasks); //newTask Form Task newTask = new Task(); model.addAttribute("newTask", newTask); Set<Status> statusList = new HashSet<>(); Status.stream().forEach(statusList::add); model.addAttribute("statusList", statusList); return "index"; } /** * Shows Tasks by Status * @param model contains TaskObject * @param taskStatus may have the values "open/closed/reopened" * @return Set of Tasks with specific status */ @RequestMapping(value = "/{status}", method=RequestMethod.GET) public String displayByStatus(Model model, @PathVariable("status") String taskStatus) { if(taskStatus.equals(Status.OPEN.getTypeOfStatus())){ model.addAttribute("tasks", taskService.getTasksByStatus(Status.OPEN)); } else if(taskStatus.equals(Status.CLOSED.getTypeOfStatus())){ model.addAttribute("tasks", taskService.getTasksByStatus(Status.CLOSED)); } else if(taskStatus.equals(Status.REOPENED.getTypeOfStatus())){ model.addAttribute("tasks", taskService.getTasksByStatus(Status.REOPENED)); } //for newTask Form Task newTask = new Task(); model.addAttribute("newTask", newTask); Set<Status> statusList = new HashSet<>(); Status.stream().forEach(statusList::add); model.addAttribute("statusList", statusList); return "index"; } /** * handles Status Changes * @param taskId Task Id * @param action may contain "close/open/reopen" * @param request helps redirect to previous site * @return redirection */ @RequestMapping(value = "/task/{id}/{action}", method=RequestMethod.GET) public String handleStatus(@PathVariable("id") Long taskId, @PathVariable("action") String action, HttpServletRequest request){ Task task = taskService.findById(taskId); if (action.equals("close")) { if(task.getStatus() == Status.OPEN) {taskService.closeTask(taskId);} if(task.getStatus() == Status.REOPENED) {taskService.closeTask(taskId);} } if(action.equals("reopen") && task.getStatus() == Status.CLOSED) {taskService.reopenTask(taskId);} String referer = request.getHeader("Referer"); return "redirect:"+ referer; } /** * Save NEW Task in Database * @param taskDetails field values * @return redirect to Dashboard */ @RequestMapping(path = "/task/create", method = RequestMethod.POST) public String createTask(Task taskDetails) { Task newTask = taskService.createTask(taskDetails); return "redirect:/"; } /** * Displays an EDIT Form for a Task * @param model task Object * @param taskId Id of the Task * @return edit Form */ @RequestMapping(value = "/task/{id}/edit", method=RequestMethod.GET) public String editForm(Model model, @PathVariable("id") Long taskId) { Set<Status> statusList = new HashSet<>(); Status.stream().forEach(statusList::add); model.addAttribute("statusList", statusList); model.addAttribute("editTask", taskService.findById(taskId)); return "editView"; } /** * Update Task and save changes in Database * @param taskId TaskId * @param taskDetails field values * @return redirect to Dashboard */ @RequestMapping(path = "/task/{id}/update", method = RequestMethod.POST) public String updateTask(@PathVariable("id") long taskId, Task taskDetails) { taskService.updateTask(taskId, taskDetails); return "redirect:/"; } /** * Deletes Task from Database * @param taskId TaskId * @return redirect to Dashboard */ @RequestMapping(path = "/task/{id}/delete", method = RequestMethod.GET) public String deleteTask(@PathVariable("id") long taskId, HttpServletRequest request) { taskService.deleteTask(taskId); return "redirect:/"; } }
Now the Backend is completed. For the Frontend create two new folders “fragments” and “tasks” in the templates folder. The fragments folder will contain snippets like the head and footer of the index.html. In tasks we’ll create snippets for editing files and creating new Tasks.
index.html
Copy the following into the index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <th:block th:include="fragments/header"></th:block> <body id="page-top"> <!-- Page Wrapper --> <div id="wrapper"> <th:block th:include="fragments/sidebarLeft"></th:block> <!-- Content Wrapper --> <div id="content-wrapper" class="d-flex flex-column"> <!-- Main Content --> <div id="content"> <th:block th:include="fragments/navbar"></th:block> <!-- Begin Page Content --> <div class="container-fluid"> <!-- Content Row --> <div class="row"> <!-- List of Tasks--> <th:block th:include="tasks/tasks"></th:block> <!-- New Task --> <th:block th:include="tasks/newTask"></th:block> </div> </div> <!-- /.container-fluid --> </div> <!-- End of Main Content --> <th:block th:include="fragments/footer"></th:block> </div> <!-- End of Content Wrapper --> </div> <!-- End of Page Wrapper --> <!-- Scroll to Top Button--> <a class="scroll-to-top rounded" href="#page-top"> <i class="fas fa-angle-up"></i> </a> <th:block th:include="fragments/scripts"></th:block> </body> </html>
In the “tasks” folder create the following files:
tasks.html:
<div class="col-xl-8 col-lg-7"> <div th:if="${#lists.isEmpty(tasks)}" class="card shadow mb-4"> <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> <h6 class="m-0 font-weight-bold text-primary"> </h6> </div> <!-- Card Body --> <div class="card-body"> <p>Your Task List is empty.</p> </div> </div> <div th:each = "task : ${tasks}" class="card shadow mb-4"> <!-- Card Header - Dropdown --> <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> <!-- Info Button for OPEN Tasks--> <div th:if="${task.status == T(com.example.taskapp.model.Status).OPEN}" class="btn btn-primary btn-icon-split"> <span class="icon text-white-50"> OPEN </span> <span class="text" th:text="${task.title}"></span> </div> <!-- Info Button for CLOSED Tasks--> <div th:if="${task.status == T(com.example.taskapp.model.Status).CLOSED}" class="btn btn-success btn-icon-split"> <span class="icon text-white-50"> CLOSED </span> <span class="text" th:text="${task.title}"></span> </div> <!-- Info Button for REOPENED Tasks--> <div th:if="${task.status == T(com.example.taskapp.model.Status).REOPENED}" class="btn btn-warning btn-icon-split"> <span class="icon text-white-50"> REOPENED </span> <span class="text" th:text="${task.title}"></span> </div> <h6 class="m-0 font-weight-bold text-primary"> </h6> <!-- Dropdown --> <div class="dropdown no-arrow"> <a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i> </a> <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink"> <div class="dropdown-header">Actions:</div> <div th:if="${task.status == T(com.example.taskapp.model.Status).OPEN}"> <a class="dropdown-item" th:href="@{/task/{id}/{action}(id=${task.id}, action='close')}">CLOSE</a> </div> <div th:if="${task.status == T(com.example.taskapp.model.Status).CLOSED}"> <a class="dropdown-item" th:href="@{/task/{id}/{action}(id=${task.id}, action='reopen')}">REOPEN</a> </div> <div th:if="${task.status == T(com.example.taskapp.model.Status).REOPENED}"> <a class="dropdown-item" th:href="@{/task/{id}/{action}(id=${task.id}, action='close')}">CLOSE</a> </div> <div class="dropdown-divider"></div> <a class="dropdown-item" th:href="@{/task/{id}/edit(id=${task.id})}"><i class="far fa-edit"></i> Edit</a> <a class="dropdown-item" th:href="@{/task/{id}/delete(id=${task.id})}"><i class="fas fa-trash-alt"></i> Delete</a> </div> </div> </div> <!-- Card Body --> <div class="card-body"> <p th:text="${task.content}"></p> </div> </div> </div>
newTask.html:
<div class="col-xl-4 col-lg-5"> <div class="card shadow mb-4"> <!-- Card Header - Dropdown --> <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> <h6 class="m-0 font-weight-bold text-primary">New Task</h6> </div> <!-- Card Body --> <div class="card-body"> <form th:object="${newTask}" th:action="@{/task/create}" th:method="post"> <div class="form-group"> <label for="title">Title:</label> <input th:field="*{title}" type="text" class="form-control" id="title"> </div> <div class="form-group"> <label for="status">Status:</label> <select id="status" class="form-control" th:field="*{status}"> <option th:each="status : ${statusList}" th:text="${status}" th:value="${status}"></option> </select> </div> <div class="form-group"> <label for="taskContent">Content:</label> <textarea th:field="*{content}" class="form-control" id="taskContent" rows="5"></textarea> </div> <button type="submit" class="btn btn-primary">Save</button> </form> </div> </div> </div>
editTask.html:
<div class="col-xl-8 col-lg-7"> <div class="card shadow mb-4"> <!-- Card Header - Dropdown --> <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> <h6 class="m-0 font-weight-bold text-primary">Edit Task</h6> </div> <!-- Card Body --> <div class="card-body"> <form th:object="${editTask}" th:action="@{/task/{id}/update(id=${editTask.id})}" th:method="post"> <div class="form-group"> <label for="title">Title:</label> <input th:field="*{title}" type="text" class="form-control" id="title"> </div> <div class="form-group"> <label for="status">Status:</label> <select id="status" class="form-control" th:field="*{status}"> <option th:each="status : ${statusList}" th:text="${status}" th:value="${status}"></option> </select> </div> <div class="form-group"> <label for="taskContent">Content:</label> <textarea th:field="*{content}" class="form-control" id="taskContent" rows="5"></textarea> </div> <button type="submit" class="btn btn-primary">Save</button> <a th:href="@{/task/{id}/delete(id=${editTask.id})}" class="btn btn-danger">Delete</a> </form> </div> </div> </div>
Next, in the fragments folder:
header.html:
<head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>TaskApp</title> <!-- Custom fonts for this template--> <link th:href="@{/vendor/fontawesome-free/css/all.min.css}" rel="stylesheet" type="text/css"> <link th:href="@{https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i}" rel="stylesheet"> <!-- Custom styles for this template--> <link th:href="@{/css/sb-admin-2.min.css}" rel="stylesheet"> </head>
navbar.html:
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow"> <!-- Sidebar Toggle (Topbar) --> <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3"> <i class="fa fa-bars"></i> </button> <!-- Topbar Navbar --> <ul class="navbar-nav ml-auto"></ul> </nav>
sidebarLeft.html:
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar"> <!-- Sidebar - Brand --> <a class="sidebar-brand d-flex align-items-center justify-content-center" th:href="@{/}"> <div class="sidebar-brand-text mx-3">TaskApp</div> </a> <!-- Divider --> <hr class="sidebar-divider my-0"> <!-- Nav Item - Dashboard --> <li class="nav-item"> <a class="nav-link" th:href="@{/}"> <i class="fas fa-fw fa-tachometer-alt"></i> <span>Dashboard</span></a> </li> <!-- Divider --> <hr class="sidebar-divider"> <!-- Heading --> <div class="sidebar-heading"> Tasks by Status </div> <!-- Nav Item - Charts --> <li class="nav-item"> <a class="nav-link" th:href="@{/{status}(status='open')}"> <i class="fas fa-fw fa-chart-area"></i> <span>OPEN</span></a> </li> <!-- Nav Item - Charts --> <li class="nav-item"> <a class="nav-link" th:href="@{/{status}(status='reopened')}"> <i class="fas fa-fw fa-chart-area"></i> <span>REOPENED</span></a> </li> <!-- Nav Item - Tables --> <li class="nav-item"> <a class="nav-link" th:href="@{/{status}(status='closed')}"> <i class="fas fa-fw fa-table"></i> <span>CLOSED</span></a> </li> <!-- Divider --> <hr class="sidebar-divider d-none d-md-block"> <!-- Sidebar Toggler (Sidebar) --> <div class="text-center d-none d-md-inline"> <button class="rounded-circle border-0" id="sidebarToggle"></button> </div> </ul>
footer.html:
<footer class="sticky-footer bg-white"> <div class="container my-auto"> <div class="copyright text-center my-auto"> <span>Copyright © <script>document.write(new Date().getFullYear())</script> platoiscoding.com</span> </div> <div class="copyright pull-right"> </div> </div> </footer>
scripts.html:
<!-- Bootstrap core JavaScript--> <script th:src="@{/vendor/jquery/jquery.min.js}"></script> <script th:src="@{/vendor/bootstrap/js/bootstrap.bundle.min.js}"></script> <!-- Core plugin JavaScript--> <script th:src="@{/vendor/jquery-easing/jquery.easing.min.js}"></script> <!-- Custom scripts for all pages--> <script th:src="@{/js/sb-admin-2.min.js}"></script>
At last, create the file “editView.html” in the same directory as index.html. It will contain the view for editing Tasks.
editView.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <th:block th:include="fragments/header"></th:block> <body id="page-top"> <!-- Page Wrapper --> <div id="wrapper"> <th:block th:include="fragments/sidebarLeft"></th:block> <!-- Content Wrapper --> <div id="content-wrapper" class="d-flex flex-column"> <!-- Main Content --> <div id="content"> <th:block th:include="fragments/navbar"></th:block> <!-- Begin Page Content --> <div class="container-fluid"> <!-- Content Row --> <div class="row"> <!-- edit Task--> <th:block th:include="tasks/editTask"></th:block> </div> </div> <!-- /.container-fluid --> </div> <!-- End of Main Content --> <th:block th:include="fragments/footer"></th:block> </div> <!-- End of Content Wrapper --> </div> <!-- End of Page Wrapper --> <!-- Scroll to Top Button--> <a class="scroll-to-top rounded" href="#page-top"> <i class="fas fa-angle-up"></i> </a> <th:block th:include="fragments/scripts"></th:block> </body> </html>
Type the following command to run the application: mvn spring-boot:run
Deploy your App to Heroku:
application.properties.file: Delete all and place the following
## Heroku Properties spring.datasource.url=${JDBC_DATABSE_URL} spring.datasource.username=${JDBC_DATABSE_USERNAME} spring.datasource.password=${JDBC_DATABSE_PASSWORD} spring.jpa.show-sql = false spring.jpa.generate.ddl = true spring.jpa.hibernate.ddl-auto = create spring.datasource.initialization-mode=always
Next open the command line runner and navigate to your application folder
(1) enter: “heroku login” and provide your email and password (you need to have an account on Heroku by now)
(2) then: “git init” and “git add .” which will add all your files to the version control
(3) git commit -m “your commit message”
(4) heroku create “name of your app”
(5) git push heroku master
(6) heroku open – will open your deployed app in your browser
Finished. Thanks for reading. This is my first tutorial, so if you run into problems please let me know.