Spring Boot Thymeleaf File Upload example
Tags: webdev, java, springboot, programming
Published: 2023-08-31 01:17:41

In this tutorial, I will show you how to implement file upload with Thymeleaf and Bootstrap in a Spring Boot project. We also use Spring Web <code>MultipartFile</code> interface to handle HTTP <a href="https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html">multi-part</a> requests.

Related Posts:
- <a href="https://www.bezkoder.com/spring-boot-image-upload-thymeleaf/">Spring Boot upload Image and Display with Thymeleaf</a>
- <a href="https://www.bezkoder.com/spring-boot-thymeleaf-example/">Spring Boot Thymeleaf CRUD example</a>
- <a href="https://www.bezkoder.com/thymeleaf-pagination-and-sorting-example/">Spring Boot Thymeleaf Pagination and Sorting example</a>
- <a href="https://www.bezkoder.com/thymeleaf-multiple-file-upload/">Spring Boot Thymeleaf Multiple File upload</a>
- <a href="https://www.bezkoder.com/spring-boot-controlleradvice-exceptionhandler/">Spring Boot @ControllerAdvice & @ExceptionHandler example</a>
- <a href="https://www.bezkoder.com/spring-boot-restcontrolleradvice/">@RestControllerAdvice example in Spring Boot</a>
- <a href="https://www.bezkoder.com/spring-boot-unit-test-jpa-repo-datajpatest/">Spring Boot Unit Test for JPA Repository</a>
- <a href="https://www.bezkoder.com/spring-boot-webmvctest/">Spring Boot Unit Test for Rest Controller</a>
- <a href="https://www.bezkoder.com/deploy-spring-boot-aws-eb/">Deploy Spring Boot App on AWS – Elastic Beanstalk</a>

Fullstack:
- <a href="https://www.bezkoder.com/angular-14-spring-boot-file-upload/">Angular + Spring Boot: File upload example</a>
- <a href="https://www.bezkoder.com/react-file-upload-spring-boot/">React + Spring Boot: File upload example</a>

Associations:
- <a href="https://www.bezkoder.com/jpa-one-to-one/">Spring Boot One To One example with JPA, Hibernate</a>
- <a href="https://www.bezkoder.com/jpa-one-to-many/">Spring Boot One To Many example with JPA, Hibernate</a>
- <a href="https://www.bezkoder.com/jpa-many-to-many/">Spring Boot Many to Many example with JPA, Hibernate</a>

<h2><span id="Overview">Thymeleaf File Upload Overview</span></h2>
Our Spring Boot + Thymeleaf File Upload example will have following features:
<ul>
	<li>uploading File to a static folder in the Server</li>
	<li>downloading File from server with the link</li>
	<li>getting list of Files' information (file name & url)</li>
</ul>

- Here is the File Upload Form:

<img src="https://www.bezkoder.com/wp-content/uploads/thymeleaf-file-upload-example.png" alt="thymeleaf-file-upload-example" width="630" height="450" class="alignnone size-full wp-image-13002" />

- If the File exceeds specific maximum size:

<img src="https://www.bezkoder.com/wp-content/uploads/thymeleaf-file-upload-exception.png" alt="thymeleaf-file-upload-exception" width="510" height="180" class="alignnone size-full wp-image-13015" />

- This is the static folder that stores all uploaded files:

<img src="https://www.bezkoder.com/wp-content/uploads/thymeleaf-file-upload-folder.png" alt="thymeleaf-file-upload-folder" width="150" height="80" class="alignnone size-full wp-image-13005" />

- You can see list of uploaded files with download links:

<img src="https://www.bezkoder.com/wp-content/uploads/thymeleaf-file-upload-spring-boot.png" alt="thymeleaf-file-upload-spring-boot" width="690" height="360" class="alignnone size-full wp-image-13004" />

In this tutorial, I don't explain way to delete File. If you want to know that, just visit:
<a href="https://www.bezkoder.com/spring-boot-delete-file-thymeleaf/">Spring Boot Delete File example with Thymeleaf</a>

Or adding Pagination with following tutorial:
<a href="https://www.bezkoder.com/thymeleaf-pagination/">Spring Boot Thymeleaf Pagination example</a>

<h2><span id="Project_Structure">Project Structure</span></h2>
<img src="https://www.bezkoder.com/wp-content/uploads/thymeleaf-file-upload-spring-boot-project.png" alt="thymeleaf-file-upload-spring-boot-project" width="330" height="640" class="alignnone size-full wp-image-13007" />

Let me explain it briefly.

- <code>FileInfo</code> contains information of the uploaded file.
- <code>FilesStorageService</code> helps us to initialize storage, save new file, load file, get list of Files' info, delete files.
- <code>FileController</code> uses <code>FilesStorageService</code> to handle file upload/download and template requests.
- <code>FileUploadExceptionAdvice</code> handles exception when the controller processes file upload.
- <code>template</code> stores HTML template files for the project.
- <em>application.properties</em> contains configuration for Servlet Multipart.
- <em>uploads</em> is the static folder for storing files.
- <em>pom.xml</em> for Spring Boot dependency.

<h2><span id="Setup">Create &#038; Setup Spring Boot project</span></h2>
Use <a href="http://start.spring.io/">Spring web tool</a> or your development tool (<a href="https://spring.io/tools">Spring Tool Suite</a>, Eclipse, <a href="https://www.jetbrains.com/idea/download/">Intellij</a>) to create a Spring Boot project.

Then open <strong>pom.xml</strong> and add dependencies for Spring Web, Thymeleaf, Bootstrap, Jquery:
```html
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>bootstrap</artifactId>
	<version>4.6.2</version>
</dependency>

<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>jquery</artifactId>
	<version>3.6.1</version>
</dependency>

<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>webjars-locator-core</artifactId>
</dependency>
```

<h2><span id="Create_Service">Create Service for File Storage</span></h2>
First we need an interface that will be autowired in the Controller.
In <em>service</em> folder, create <code>FilesStorageService</code> interface like following code:

<em>service/FilesStorageService.java</em>
```java
package com.bezkoder.spring.thymeleaf.file.upload.service;

import java.nio.file.Path;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

public interface FilesStorageService {
  public void init();

  public void save(MultipartFile file);

  public Resource load(String filename);
  
  public void deleteAll();

  public Stream<Path> loadAll();
}
```

Now we create implementation of the interface.

<em>service/FilesStorageServiceImpl.java</em>
```java
package com.bezkoder.spring.thymeleaf.file.upload.service;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

@Service
public class FilesStorageServiceImpl implements FilesStorageService {
  private final Path root = Paths.get("./uploads");

  @Override
  public void init() {
    try {
      Files.createDirectories(root);
    } catch (IOException e) {
      throw new RuntimeException("Could not initialize folder for upload!");
    }
  }

  @Override
  public void save(MultipartFile file) {
    try {
      Files.copy(file.getInputStream(), this.root.resolve(file.getOriginalFilename()));
    } catch (Exception e) {
      if (e instanceof FileAlreadyExistsException) {
        throw new RuntimeException("A file of that name already exists.");
      }

      throw new RuntimeException(e.getMessage());
    }
  }

  @Override
  public Resource load(String filename) {
    try {
      Path file = root.resolve(filename);
      Resource resource = new UrlResource(file.toUri());

      if (resource.exists() || resource.isReadable()) {
        return resource;
      } else {
        throw new RuntimeException("Could not read the file!");
      }
    } catch (MalformedURLException e) {
      throw new RuntimeException("Error: " + e.getMessage());
    }
  }

  @Override
  public void deleteAll() {
    FileSystemUtils.deleteRecursively(root.toFile());
  }

  @Override
  public Stream<Path> loadAll() {
    try {
      return Files.walk(this.root, 1).filter(path -> !path.equals(this.root)).map(this.root::relativize);
    } catch (IOException e) {
      throw new RuntimeException("Could not load the files!");
    }
  }
}
```

<h2><span id="Create_Controller">Create Controller for File Upload</span></h2>
In <strong>controller</strong> package, we create <code>FileController</code>.

<em>controller/FileController.java</em>
```java
package com.bezkoder.spring.thymeleaf.file.upload.controller;
// ...
import com.bezkoder.spring.thymeleaf.file.upload.service.FilesStorageService;

@Controller
public class FileController {

  @Autowired
  FilesStorageService storageService;

  @GetMapping("/")
  public String homepage() {
    return "redirect:/files";
  }

  @GetMapping("/files/new")
  public String newFile(Model model) {
    return "upload_form";
  }

  @PostMapping("/files/upload")
  public String uploadFile(Model model, @RequestParam("file") MultipartFile file) {
    ...

    return "upload_form";
  }

  @GetMapping("/files")
  public String getListFiles(Model model) {
    ...

    return "files";
  }

  @GetMapping("/files/{filename:.+}")
  public ResponseEntity<Resource> getFile(@PathVariable String filename) {
    ...

    // return File
  }
}
```

- <code>@Controller</code> annotation is used to define a controller.
- <code>@GetMapping</code> and <code>@PostMapping</code> annotation is for mapping HTTP GET & POST requests onto specific handler methods and returning appropriate template files.
- We use <code>@Autowired</code> to inject implementation of <code>FilesStorageService</code> bean to local variable.

<h2><span id="Setup_template">Setup the Template</span></h2>
In <strong>src</strong>/<strong>main</strong>/<strong>resources</strong> folder, create folder and file as following structure:

<img src="https://www.bezkoder.com/wp-content/uploads/thymeleaf-file-upload-example-template.png" alt="thymeleaf-file-upload-example-template" width="210" height="150" class="alignnone size-full wp-image-13012" />

<h3>Header and Footer</h3>
We will use Thymeleaf Fragments (<code>th:fragment</code>) to reuse some common parts such as header and footer.
Let's write HTML code for them.

<strong>fragments</strong>/<em>footer.html</em>
```html
<footer class="text-center">
  Copyright &copy; BezKoder
</footer>
```

And header that contains the navigation bar:
<strong>fragments</strong>/<em>header.html</em>
```html
<header th:fragment="header">
  <nav class="navbar navbar-expand-md bg-dark navbar-dark mb-3">
    <a class="navbar-brand" th:href="@{/files}">
      BezKoder
    </a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#topNavbar">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="topNavbar">
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link" th:href="@{/files/new}">Upload</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" th:href="@{/files}">Files</a>
        </li>
      </ul>
    </div>
  </nav>
</header>
```

Now we need to create HTML files, then import Thymeleaf fragments, Bootstrap, jQuery and Font Awesome.

<h3>File Upload Form</h3>

<em>upload_form.html</em>
```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0" />
  <title>BezKoder - Thymeleaf File Upload example</title>
  <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
  <script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
  <script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>

<body>
  <div th:replace="fragments/header :: header"></div>

  -- file upload form --

  <div th:replace="fragments/footer :: footer"></div>
</body>

</html>
```

<h3>List of Files</h3>

<em>files.html</em>
```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0" />
  <title>BezKoder - Thymeleaf File Upload example</title>

  <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
    integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
    crossorigin="anonymous" referrerpolicy="no-referrer" />
  <script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
  <script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>

<body>
  <div th:replace="fragments/header :: header"></div>

  -- list of files display --
  
  <div th:replace="fragments/footer :: footer"></div>
</body>

</html>
```

<h2><span id="Implement_File_Upload">Implement File Upload function</span></h2>
We use <code>@GetMapping</code> and <code>@PostMapping</code> annotation is for mapping HTTP GET & POST requests onto specific handler methods:
<ul>
	<li>GET /files/new: <code>newFile()</code> - return <em>upload_form.html</em> template</li>
	<li>POST /files/upload: <code>uploadFile()</code> - upload a File</li>
</ul>

<em>FileController.java</em>
```java
import com.bezkoder.spring.thymeleaf.file.upload.service.FilesStorageService;

@Controller
public class FileController {

  @Autowired
  FilesStorageService storageService;

  @GetMapping("/files/new")
  public String newFile(Model model) {
    return "upload_form";
  }

  @PostMapping("/files/upload")
  public String uploadFile(Model model, @RequestParam("file") MultipartFile file) {
    String message = "";

    try {
      storageService.save(file);

      message = "Uploaded the file successfully: " + file.getOriginalFilename();
      model.addAttribute("message", message);
    } catch (Exception e) {
      message = "Could not upload the file: " + file.getOriginalFilename() + ". Error: " + e.getMessage();
      model.addAttribute("message", message);
    }

    return "upload_form";
  }
}
```

<em>upload_form.html</em>
```html
<div class="container" style="max-width: 500px">
  <h3 class="mb-3">Thymeleaf File Upload example</h3>

  <form
    id="uploadForm"
    method="post"
    th:action="@{/files/upload}"
    enctype="multipart/form-data">
    <input id="input-file" type="file" name="file" />
    <button class="btn btn-sm btn-outline-success float-right" type="submit">
      Upload
    </button>
  </form>

  <div
    th:if="${message != null}"
    class="alert alert-secondary alert-dismissible fade show text-center message mt-3"
    role="alert">
    [[${message}]]
    <button type="button" class="close btn-sm" data-dismiss="alert" aria-label="Close">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>
</div>
```

<h2><span id="Display_List_Files">Display List of Files</span></h2>
Firstly, we need to create <code>FileInfo</code> model which has fields: <code>name</code> & <code>url</code>.

<em>model/FileInfo.java</em>
```java
package com.bezkoder.spring.thymeleaf.file.upload.model;

public class FileInfo {
  
  private String name;
  private String url;

  public FileInfo(String name, String url) {
    this.name = name;
    this.url = url;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getUrl() {
    return this.url;
  }

  public void setUrl(String url) {
    this.url = url;
  }
}
```

In the Controller, we will return List of <code>FileInfo</code> objects as model Attribute. 
<code>@GetMapping</code> and <code>@PostMapping</code> annotation is for mapping HTTP GET & POST requests onto specific handler methods:
<ul>
	<li>GET /files: <code>getListFiles()</code> - return <em>files.html</em> template</li>
	<li>GET /files/[filename]: <code>getFile()</code> - download a File by <code>filename</code></li>
</ul>

<em>FileController.java</em>
```java
import com.bezkoder.spring.thymeleaf.file.upload.model.FileInfo;
import com.bezkoder.spring.thymeleaf.file.upload.service.FilesStorageService;

@Controller
public class FileController {

  @Autowired
  FilesStorageService storageService;

  // ...

  @GetMapping("/")
  public String homepage() {
    return "redirect:/files";
  }

  @GetMapping("/files")
  public String getListFiles(Model model) {
    List<FileInfo> fileInfos = storageService.loadAll().map(path -> {
      String filename = path.getFileName().toString();
      String url = MvcUriComponentsBuilder
          .fromMethodName(FileController.class, "getFile", path.getFileName().toString()).build().toString();

      return new FileInfo(filename, url);
    }).collect(Collectors.toList());

    model.addAttribute("files", fileInfos);

    return "files";
  }

  @GetMapping("/files/{filename:.+}")
  public ResponseEntity<Resource> getFile(@PathVariable String filename) {
    Resource file = storageService.load(filename);

    return ResponseEntity.ok()
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").body(file);
  }
}
```

<em>files.html</em>
```html
<div class="container-fluid" style="max-width: 600px; margin: 0 auto;">
  <h2 class="text-center">List of Files</h2>

  <div th:if="${files.size() > 0}">
    <table class="table table-hover">
      <thead class="thead-light">
        <tr>
          <th scope="col">File Name</th>
          <th scope="col">Link</th>
          <th scope="col">Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr th:each="file : ${files}">
          <td>[[${file.name}]]</td>
          <td><a th:href="@{${file.url}}">Download</a></td>
          <td>
            <a th:href="@{'/files/delete/' + ${file.name}}" th:fileName="${file.name}" id="btnDelete"
              title="Delete this file" class="fa-regular fa-trash-can icon-dark btn-delete"></a>
          </td>
        </tr>
      </tbody>
    </table>
  </div>

  <div th:unless="${files.size() > 0}">
    <span>No files found!</span>
  </div>
</div>
```

For deleting file, kindly visit following tutorial:
<a href="https://www.bezkoder.com/spring-boot-delete-file-thymeleaf/">Spring Boot Delete File example with Thymeleaf</a>

<h2><span id="Configure">Configure Multipart File for Servlet</span></h2>
Let's define the maximum file size that can be uploaded in <em>application.properties</em> as following: 

```properties
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=1MB
```
– <code>spring.servlet.multipart.max-file-size</code>: max file size for each request.
– <code>spring.servlet.multipart.max-request-size</code>: max request size for a multipart/form-data.

<h2><span id="Handle_Exception">Handle File Upload Exception</span></h2>
This is where we handle the case in that a request exceeds Max Upload Size. The system will throw <code>MaxUploadSizeExceededException</code> and we're gonna use <code>@ControllerAdvice</code> with <code>@ExceptionHandler</code>annotation for handling the exceptions.

<em>exception/FileUploadExceptionAdvice.java</em>
```java
package com.bezkoder.spring.thymeleaf.file.upload.exception;

import org.springframework.web.multipart.MaxUploadSizeExceededException;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class FileUploadExceptionAdvice {

  @ExceptionHandler(MaxUploadSizeExceededException.class)
  public String handleMaxSizeException(Model model, MaxUploadSizeExceededException e) {
    model.addAttribute("message", "File is too large!");

    return "upload_form";
  }
}
```

<h2><span id="Initialize_Storage">Initialize Storage</span></h2>
We need to run <code>init()</code> method of <code>FilesStorageService</code> (and also <code>deleteAll()</code> if necessary). So open <em>ThymeleafFileUploadApplication.java</em> and implement <code>CommandLineRunner</code> for <code>run()</code> method like this:

```java
package com.bezkoder.spring.thymeleaf.file.upload;

import javax.annotation.Resource;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.bezkoder.spring.thymeleaf.file.upload.service.FilesStorageService;

@SpringBootApplication
public class ThymeleafFileUploadApplication implements CommandLineRunner {

  @Resource
  FilesStorageService storageService;

  public static void main(String[] args) {
    SpringApplication.run(ThymeleafFileUploadApplication.class, args);
  }

  @Override
  public void run(String... arg) throws Exception {
//    storageService.deleteAll();
    storageService.init();
  }
}
```

<h2><span id="Run">Run the Spring Boot File Upload example</span></h2>
Run Spring Boot application with command: <code>mvn spring-boot:run</code>.

<h2><span id="Source_Code">Source Code</span></h2>
You can find the complete source code for this tutorial on <a href="https://github.com/bezkoder/thymeleaf-file-upload">Github</a>.

For deleting file:
<a href="https://www.bezkoder.com/spring-boot-delete-file-thymeleaf/">Spring Boot Delete File example with Thymeleaf</a>

<h2><span id="Conclusion">Conclusion</span></h2>
Today we've learned how to create Spring Boot Thymeleaf File Upload Application with multipart files and get files' information with static folder.

For upload multiple Files at once:
<a href="https://www.bezkoder.com/thymeleaf-multiple-file-upload/">Spring Boot Multiple File upload with Thymeleaf</a>

Or fullstack with frontend:
- <a href="https://www.bezkoder.com/angular-14-spring-boot-file-upload/">Angular + Spring Boot: File upload example</a>
- <a href="https://www.bezkoder.com/react-file-upload-spring-boot/">React + Spring Boot: File upload example</a>

You can also know way to upload an Excel/CSV file and store the content in MySQL database with the post:
- <a href="https://www.bezkoder.com/spring-boot-upload-excel-file-database/">Spring Boot: Upload/Import Excel file data into MySQL Database</a>
- <a href="https://www.bezkoder.com/spring-boot-upload-csv-file/">Spring Boot: Upload/Import CSV file data into MySQL Database</a>

If you want to store files in database like this:
<img src="https://bezkoder.com/wp-content/uploads/2020/07/spring-boot-upload-files-to-database-table-files.png" alt="spring-boot-upload-files-to-database-table-files" width="450" height="88" class="alignnone size-full wp-image-4086" />

You can find instruction at:
<a href="https://www.bezkoder.com/spring-boot-upload-file-database/">Spring Boot Upload/Download File to/from Database example</a>

Happy Learning! See you again.

<h2>Further Reading</h2>
- <a href="https://www.bezkoder.com/spring-boot-image-upload-thymeleaf/">Spring Boot upload Image and Display with Thymeleaf</a>
- <a href="https://www.bezkoder.com/spring-boot-upload-multiple-files/">Spring Boot upload multiple files Rest API</a>
- <a href="https://www.bezkoder.com/spring-boot-thymeleaf-example/">Spring Boot Thymeleaf CRUD example</a>
- <a href="https://www.bezkoder.com/thymeleaf-pagination-and-sorting-example/">Spring Boot Thymeleaf Pagination and Sorting example</a>

Exception Handling:
- <a href="https://www.bezkoder.com/spring-boot-controlleradvice-exceptionhandler/">Spring Boot @ControllerAdvice & @ExceptionHandler example</a>
- <a href="https://www.bezkoder.com/spring-boot-restcontrolleradvice/">@RestControllerAdvice example in Spring Boot</a>

Unit Test:
- <a href="https://bezkoder.com/spring-boot-unit-test-jpa-repo-datajpatest/">Spring Boot Unit Test for JPA Repository</a>
- <a href="https://www.bezkoder.com/spring-boot-webmvctest/">Spring Boot Unit Test for Rest Controller</a>

Deployment:
- <a href="https://www.bezkoder.com/deploy-spring-boot-aws-eb/">Deploy Spring Boot App on AWS – Elastic Beanstalk</a>

Associations:
- <a href="https://www.bezkoder.com/jpa-one-to-one/">Spring Boot One To One example with JPA, Hibernate</a>
- <a href="https://www.bezkoder.com/jpa-one-to-many/">Spring Boot One To Many example with JPA, Hibernate</a>
- <a href="https://www.bezkoder.com/jpa-many-to-many/">Spring Boot Many to Many example with JPA, Hibernate</a>