You are here
Home > java >

Spring Boot MVC CRUD Example

Spring Boot MVC CRUD ExampleWhen we develop a Web Application using Spring Boot, the knowledge of MVC pattern becomes important. Needless to say, the MVC is a short form of Model, View and the Controller. Moreover, the Spring Boot makes a developer’s life easier in developing the web application using MVC architecture. Spring Boot offers us a starter project ‘Spring web’ to work with MVC applications. In this article, we are going to learn entire CRUD operations that are mandatory to develop any MVC application. It will also provide you the knowledge of complete flow of MVC starting from user interface till the data layer. Hence our article title is ‘Spring Boot MVC CRUD Example’.

In this article, we will make use of Thymeleaf, Bootstrap for view part and MySQL for database. Let’s discuss our topic ‘Spring Boot MVC CRUD Example’ and the related concepts.

What all Technology & Software we used?

♦ STS (Spring Tool Suite) : Version-> 4.7.1.RELEASE
⇒ Dependent Starters : ‘Spring Web’, ‘Spring Data JPA’, ‘MySQL Driver’, ‘Thymeleaf’, ‘Lombok’ and ‘Spring Boot DevTools’
♥ User Interface : Thymeleaf, Bootstrap, Font-Awesome
♦ MySQL Database : Version ->8.0.19 MySQL Community Server
♦ JDK8 or later versions (Extremely tested on JDK8, JDK9 and JDK14)

What is MVC?

Model–View–Controller (usually known as MVC) is a software design pattern popularly used for developing web applications. It divides the related programming logic into three interrelated components. The three components are Model, View, and the Controller.

Model

Model is the central component of the pattern. It is the application’s dynamic data structure, independent of the user interface. Model directly manages the data, logic and business rules of the application.

View

Any representation of information that an end user can see, such as forms, text boxes, drop-downs, charts, diagrams or tables. Multiple views of the same information are also possible, such as a bar chart, pie chart and a tabular representation for a particular data. Even excel and pdf reports are also considered as part of the View.

Controller

The controller accepts requests from the user and converts it into commands for the model or view to get the response for the user. Moreover, it acts as an interface between Model and View components to process all the incoming requests from the user.

Interaction among the three Components:

1) The model is responsible for managing the data of the application. It receives user input from the controller.
2) The view renders presentation of the model in a particular format.
3) The controller responds to the user input and performs interactions with the data model objects. Moreover, it receives the input, optionally validates it and then passes the input to the model.

Why to use the MVC Pattern?

There are multiple reasons to use MVC pattern. Some of them as below:

1) Separation of Concerns -Separation of Concern is one of the core advantages of an MVC Pattern. The MVC pattern provides a clean separation between the UI, Business Logic, Model or Data. On the other hand, we can also say it provides a separation of Programming logics for various components.
2) Easy to modify – Because of the separation of responsibilities, future development or modification becomes easier and in turn it increases the scalability of the software.
3) Simultaneous development — Multiple developers can work simultaneously on the model, controller and views separately as per their area of expertise.
4) Low coupling — The MVC pattern offers low coupling between its components.
5) High cohesion — It provides high cohesion such as it enables logical grouping of related actions on a controller together. The views for a specific model are also grouped together.

What all functionalities can you expect from this Spring Boot MVC CRUD example?

Below is the summary of page-wise functionalities that you can expect from this Spring Boot MVC CRUD Example.

Home Page

1) When the user hits the application URL into the browser, he/she will see the home page that is the entry point of the application. From Here, users will be able to visit the ‘Invoice registration page’ and the ‘list of All invoices’ Page by clicking on the given links.

Invoice Registration Page

2) If user clicks on the ‘Add Invoice’ link available on the home page, he/she will redirect to the invoice registration page.
3) In the Invoice registration page, the user can fill the form and save it after clicking on the ‘Save Invoice’ button. After the successful addition of the record, a message “Invoice with id: ‘XXX’ is added successfully !” will be displayed at the bottom. From this page user can also visit to list of Invoice Pages after clicking on the ‘List Of Invoices’ link.
4) If user clicks on the ‘Show All Invoices’ link available on the home page, he/she will enter to the list of invoice Pages and can see all pre-existing invoices.

List Of Invoices Page

5) In the List Of Invoice page, user can perform ‘edit’ or ‘delete’ operation on the invoices. Additionally, user can also enter into the Invoice registration page after clicking on the ‘Add Invoice’ link.
6) If user clicks on the ‘Edit’ link available at List Of Invoices page, a new form will open. User can modify the value and update it into the DB after clicking on the ‘Update’ button. After a successful update, a message “Invoice with id: ‘XXX’ is updated successfully !” will be displayed at the bottom.
7) If user clicks on the ‘Delete’ link available at List Of Invoices page, the record will be deleted. After successful removal of the record, a message “Invoice with id: ‘XXX’ is deleted successfully !” will be displayed at the bottom.

8) From the list of Invoices page, user can go back to the home page after clicking on the ‘Go to Home’ link.

Below screen represents the pictorial view of the functionalities.

Spring Boot MVC CRUD Example

What are the steps to develop a Spring Boot MVC CRUD Example?

If we develop any software following the specific steps, we minimize the chances of getting bugs. Furthermore, in case we face any issue after the implementation, we also reduce the time to fix it. Here are the common steps to develop a ‘Spring Boot MVC CRUD Example’.

Step#1: Create a starter Project using an IDE

Create a Spring Boot starter project using any IDE like STS, Netbeans, Intellij Idea etc. While creating Starter Project select ‘Spring Web’, ‘Spring Data JPA’, ‘MySQL Driver’, ‘ Thymeleaf’, ‘Lombok’ and ‘Spring Boot DevTools’ as starter project dependencies. Here ‘Lombok’ and ‘Spring Boot Dev Tools’ are optional to add.

Step#2: Update application.properties or application.yml

The next step to update either application.properties or application.yml whichever is applicable. Here we need to provide datasource details like DB driver class name, DB url, DB username and DB password. Additionally, we can provide other configuration properties such as dialect, ddl-auto, show-sql etc. However, as we are talking about a basic MVC application, our purpose to update this file is just to connect through the database.

Step#3: Create Entity (model) class

Now, it’s the first step to start coding. It’s a recommendation that we should start with Entity class.

Step#4: Create Repository Interface for DB access

In order to access the database programmatically, we need to create one Repository Interface that will extend any Repository Interface provided by Spring Data JPA such as JpaRepository, CrudRepository, PagingAndSortingRepository etc. as per our requirement. However, for a basic CRUD operation, we don’t need to write any custom method in this interface. Spring Data JPA will provide all the methods by default that we require in a standard CRUD operation.

Step#5: Create Service Interface & Service Impl classes

Now we are in the Service Layer of the application. Here, we need to create one Service Interface and its Implementation class. Annotate this class with @Service to inform Spring Container that this class will act as a service. In order to connect to Data Layer, we need to provide dependency of the Repository interface (created in Step#4) in our service implementation class via auto-wiring.

Step#6: Create Controller class

Having followed the above steps, once we complete flow of data access, at the last, we need to create a Controller class to handle requests coming from the browser. Annotate this class with @Controller to inform Spring Container that this is a controller. The controller class will have handler methods to serve the requests for performing various operations involved in CRUD with the help of other layers. Please note that the Controller class will connect to the service layer via auto-wiring of Service Interface.

Step#7: Create pages for view

In this step, we need to develop user interface part from where a user interacts with the application to perform various operations included in the CRUD. Moreover, any request made by the user from UI page will be handed over to the Controller. Subsequently, based on the request nature, the controller will connect to service layer to serve the request accordingly. However, we can develop Step#6 and Step#7 interchangeably as both are dependent on each other.

Spring Boot MVC CRUD Example

Use-case Details

Let’s assume we have to develop an Invoice Processing Application. As the application name suggests, we must have an Invoice entity in this application. In this example, we will develop CRUD operations for Invoice as an entity.

Let’s develop Spring Boot MVC CRUD Example step by step as below:

Step#1: Create a Spring Boot Starter Project using STS

Here, we are using STS (Spring tool Suite) as an IDE to develop the example. While creating Starter Project select ‘Spring Web’, ‘Spring Data JPA’, ‘MySQL Driver’, ‘Thymeleaf’, ‘Lombok’ and ‘Spring Boot DevTools’ as starter project dependencies. Here ‘Lombok’ and ‘Spring Boot Dev Tools’ are optional to add. Even If you don’t know how to create Spring Boot Starter Project, Kindly visit Internal Link.  Also, if you want to know more about Lombok, then visit Internal Link.

Step#2: Update application.properties

Let’s update application.properties that we already have after creating the starter project in step#1. For example, we need to include below entries.

application.properties

server.port=8888

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/invoice-mvc
spring.datasource.username=root
spring.datasource.password=****

spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.hibernate.ddl-auto=update

Step#3: Create Entity (model) class

Since our use-case for this example is Invoice Processing, we will create an entity class as Invoice.java as below.

Invoice.java

package com.dev.springboot.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Invoice {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String location;
    private Double amount;
}

Step#4: Create Repository Interface for DB access

Next step is to create one Repository Interface. Please note that we should create a separate repository interface for each Entity in the application. This is also applicable for classes taking part in other layers accordingly. Since we have only one Entity, we will create one Repository Interface for now. As a naming convention, we will create a repository interface as InviceRepository.java as below.

InvoiceRepository.java

package com.dev.springboot.repo;

import org.springframework.data.jpa.repository.JpaRepository;
import com.dev.springboot.model.Invoice;

public interface InvoiceRepository extends JpaRepository<Invoice, Long> {

}

Step#4A: Create a custom Exception class

This is an additional step to handle exceptions if any user search for an Invoice by Invoice Id and the same doesn’t exist at all. Let’s suppose a user search from the browser using direct URL, then there is a possibility that the invoice may not exist. In order to handle this scenario, we need to provide the user a readable message. It is possible with the help of creating a custom exception class. For example, below code demonstrates the concept of creating a custom exception.

InvoiceNotFoundException.java

package com.dev.springboot.exception;

public class InvoiceNotFoundException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    public InvoiceNotFoundException() {
        super();
    }

    public InvoiceNotFoundException(String customMessage) {
        super(customMessage);
    }
}

Step#5: Create Service Interface & Service Impl classes

As part of service layer, we need to create an interface and its implementation. Don’t forget to include @Service at the top of the service implementation class. Additionally, inject the dependency of the Repository interface via @Autowired. For example, below code demonstrates the concept behind the service layer. As a convention, the service interface should start with the letter ‘I’ to be recognized as an interface. Subsequently, the service implementation class should have a suffix ‘Impl’ in its name as shown below.

IInvoiceService.java

package com.dev.springboot.service;

import java.util.List;
import com.dev.springboot.model.Invoice;

public interface IInvoiceService {

    public Invoice saveInvice(Invoice invoice);
    public List<Invoice> getAllInvoices();
    public Invoice getInvoiceById(Long id);
    public void deleteInvoiceById(Long id);
    public void updateInvoice(Invoice invoice);

}

InvoiceServiceImpl.java

package com.dev.springboot.service.impl;

import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dev.springboot.exception.InvoiceNotFoundException;
import com.dev.springboot.model.Invoice;
import com.dev.springboot.repo.InvoiceRepository;
import com.dev.springboot.service.IInvoiceService;

@Service
public class InvoiceServiceImpl implements IInvoiceService{

    @Autowired
    private InvoiceRepository repo;

    @Override
    public Invoice saveInvice(Invoice invoice) {
       return repo.save(invoice);
    }

    @Override
    public List<Invoice> getAllInvoices() {
       return repo.findAll();
    }

    @Override
    public Invoice getInvoiceById(Long id) {
       Optional<Invoice> opt = repo.findById(id);
       if(opt.isPresent()) {
           return opt.get();
       } else {
           throw new InvoiceNotFoundException("Invoice with Id : "+id+" Not Found");
       }
    }

    @Override
    public void deleteInvoiceById(Long id) {
       repo.delete(getInvoiceById(id)); 
    }

    @Override
    public void updateInvoice(Invoice invoice) {
       repo.save(invoice);
    }
}

Step#6: Create Controller class

In order to handle various requests of a client, we need to create a controller as InvoiceController.java. Moreover, each handler method will return a UI page based on the criteria it implemented for. For example, below code snippet of InvoiceController will demonstrate the relation between UI page and the controller.

InvoiceController.java

package com.dev.springboot.controller;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.dev.springboot.exception.InvoiceNotFoundException;
import com.dev.springboot.model.Invoice;
import com.dev.springboot.service.IInvoiceService;

@Controller
@RequestMapping("/invoice")
public class InvoiceController {

    @Autowired   
    private IInvoiceService service;

    @GetMapping("/")
    public String showHomePage() {
       return "homePage";
    }

    @GetMapping("/register")
    public String showRegistration() {
       return "registerInvoicePage";
    }

    @PostMapping("/save")
    public String saveInvoice(
            @ModelAttribute Invoice invoice,
            Model model
            ) {
        service.saveInvice(invoice);
        Long id = service.saveInvice(invoice).getId();
        String message = "Record with id : '"+id+"' is saved successfully !";
        model.addAttribute("message", message);
        return "registerInvoicePage";
    }

    @GetMapping("/getAllInvoices")
    public String getAllInvoices(
            @RequestParam(value = "message", required = false) String message,
            Model model
            ) {
       List<Invoice> invoices= service.getAllInvoices();
       model.addAttribute("list", invoices);
       model.addAttribute("message", message);
       return "allInvoicesPage";
    }

    @GetMapping("/edit")
    public String getEditPage(
            Model model,
            RedirectAttributes attributes,
            @RequestParam Long id
            ) {
       String page = null; 
       try {
       Invoice invoice = service.getInvoiceById(id);
       model.addAttribute("invoice", invoice);
       page="editInvoicePage";
       } catch (InvoiceNotFoundException e) {
           e.printStackTrace();
           attributes.addAttribute("message", e.getMessage());
           page="redirect:getAllInvoices";
       }
       return page; 
    }

    @PostMapping("/update")
    public String updateInvoice(
            @ModelAttribute Invoice invoice,
            RedirectAttributes attributes
            ) {
       service.updateInvoice(invoice);
       Long id = invoice.getId();
       attributes.addAttribute("message", "Invoice with id: '"+id+"' is updated successfully !");
       return "redirect:getAllInvoices";
    }

    @GetMapping("/delete")
    public String deleteInvoice(
            @RequestParam Long id,
            RedirectAttributes attributes
            ) {
        try {
        service.deleteInvoiceById(id);
        attributes.addAttribute("message", "Invoice with Id : '"+id+"' is removed successfully!");
        } catch (InvoiceNotFoundException e) {
            e.printStackTrace();
            attributes.addAttribute("message", e.getMessage());
        }
        return "redirect:getAllInvoices";
    }
}

Step#7: Create pages for view

Last part of our example is to create UI pages that will help users to interact with the application. Here, we have four pages : homePage.html as an entry point of the application, registerInvoicePage.html to fill the form and register an Invoice, allInvoicesPages.html to see the list of registered invoices, and editInvoicePage.html to update the data of any invoice.

homePage.html

homepage.html
<html xmlns:th="https://www.thymeleaf.org/">
<head>
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" />
<script
	src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet"
	href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
</head>
<body>
<div class="col-5">
<div class="container">

		<div class="card">
			<div class="card-header bg-info text-center text-white">
				<h3>Welcome to Invoice MVC App</h3>
			</div>
			<div class="card-body">
				<form>
					<a th:href="@{/invoice/register}" class= "btn btn-success ">Add Invoice <i class="fa fa-plus-square" aria-hidden="true"></i></a>               
					<a th:href="@{/invoice/getAllInvoices}" class= "btn btn-primary">Show All Invoices</a>
				</form>
			</div>
			</div>		
		</div> </div>
</body>
</html>

registerInvoicePage.html

registerInvoicePage.html
<html xmlns:th="https://www.thymeleaf.org/">
<head>
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" />
<script
	src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet"
	href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
</head>
<body>
<div class="col-5">
<div class="container">

		<div class="card">
			<div class="card-header bg-info text-center text-white">
				<h3>Register Invoices</h3>
			</div>
			<div class="card-body">
				<form th:action="@{/invoice/save}" method="POST" id="invoiceForm">
					<div class="row">
						<div class="col-2">
							<label for="name"><b>NAME</b></label>
						</div>
						<div class="col-4">
							<input type="text" name="name" id="name"
								class="form-control" />
						</div>
					</div> <br/>
					<div class="row">
						<div class="col-2">
							<label for="location"><b>LOCATION</b></label>
						</div>
						<div class="col-4">
							<input type="text" name="location" id="location"
								class="form-control" />
						</div>
					</div><br/>
					<div class="row">
						<div class="col-2">
							<label for="amount"><b>AMOUNT</b></label>
						</div>
						<div class="col-4">
							<input type="text" name="amount" id="amount"
								class="form-control" />
						</div>
					</div> <br/>
					<button type="submit" class="btn btn-success">Save Invoice <i class="fa fa-plus-square" aria-hidden="true"></i></button>              
					<a th:href="@{/invoice/getAllInvoices}" class= "btn btn-primary">Show All Invoices</a>
				</form>
			</div>
			<div th:if="${message!=null}"  class="card-footer bg-white text-info">
				<span th:text="${message}"></span>
			</div>
			</div>		
		</div> </div>
</body>
</html>

allInvoicesPage.html

allInvoicesPage.html

<html xmlns:th="https://www.thymeleaf.org/">
<head>
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" />
<script
	src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet"
	href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
</head>

<body>
  <div class="col-5">
	<div class="container">
		<div class="card">
			<div class="card-header bg-info text-center text-white">
				<h3>List Of Invoices</h3>
			</div>
			<div class="card-body">
				<table class="table table-hover">
					<tr class="bg-dark text-white">
						<th>ID</th>
						<th>Name</th>
						<th>Location</th>
						<th>Amount</th>
						<th>Edit/Delete</th>
					</tr>
					<tr th:each="ob:${list}">
						<td th:text=${ob.id}></td>
						<td th:text=${ob.name}></td>
						<td th:text=${ob.location}></td>
						<td th:text=${ob.amount}></td>
						<td><a th:href="@{/invoice/delete(id=${ob.id})}" class= "btn btn-danger">DELETE <i class="fa fa-trash-o" aria-hidden="true"></i></a> |
						    <a th:href="@{/invoice/edit(id=${ob.id})}" class="btn btn-warning">EDIT <i class="fa fa-pencil-square-o" aria-hidden="true"></i></a></td>
					</tr>
				</table>
				<a th:href="@{/invoice/register}" class= "btn btn-success ">Add Invoice <i class="fa fa-plus-square" aria-hidden="true"></i></a> 
				<a th:href="@{/invoice/}" class= "btn btn-primary">Go to Home</a>
			</div>
			<div class="card-footer bg-white text-success" th:if="${message!=null}">
				<span th:text="${message}"></span>
			</div>
		</div>
	</div></div>
</body>
</html>

editInvoicePage.html

editInvoicePage.html
<html xmlns:th="https://www.thymeleaf.org/">
  <head>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
    />
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css"
    />
  </head>
  <body>
  <div class="col-5">
    <div class="container">
      <div class="card">
        <div class="card-header bg-primary text-white text-center">
            <h3>Edit Invoice</h3>
        </div>
        <div class="card-body">
            <form th:action="@{/invoice/update}" method="POST" id="invoiceForm" th:object="${invoice}">
                <div class="row">
                    <div class="col-2">
                        <label for="id">ID</label>
                    </div>
                    <div class="col-4">
                        <input type="text" th:field="*{id}" class="form-control" readonly/>
                    </div>
                </div><br/>
                <div class="row">
                    <div class="col-2">
                        <label for="name">NAME</label>
                    </div>
                    <div class="col-4">
                        <input type="text" th:field="*{name}" class="form-control"/>
                    </div>
                </div><br/>
                <div class="row">
                    <div class="col-2">
                        <label for="location">LOCATION</label>
                    </div>
                    <div class="col-4">
                        <input type="text" th:field="*{location}" class="form-control"/>
                    </div>
                </div><br/>
                <div class="row">
                    <div class="col-2">
                        <label for="amount">AMOUNT</label>
                    </div>
                    <div class="col-4">
                        <input type="text" th:field="*{amount}" class="form-control" />
                    </div>
                </div> <br/>
                <button type="submit" class="btn btn-success">Update <i class="fa fa-wrench" aria-hidden="true"></i></button>             
				<a th:href="@{/invoice/getAllInvoices}" class= "btn btn-primary">Show All Invoices</a>
            </form>
        </div>
        <div class="card-footer"></div>
      </div>
    </div></div>
  </body>
</html>

The structure of the project should look like below:

Spring Boot MVC

How to test the application?

In order to test the application, we need to open a browser and hit the URL http://localhost:8888/invoice/. On hitting the URL, home page will get displayed. Further, follow the instruction given in the ‘What all functionalities can you expect from this Spring Boot MVC CRUD example?’ section of this article to test all other functionalities accordingly.

Conclusion

In this article we have covered all the theoretical and example part of ‘Spring Boot MVC CRUD example’, finally, you should be able to build an MVC application using Spring Boot. Similarly, we expect from you to further extend this example, as per your requirement. Also try to implement it in your project accordingly. Moreover, Feel free to provide your comments in the comments section below.

close

4 thoughts on “Spring Boot MVC CRUD Example

  1. Nice article but it would be great if you can show the project structure to understand which file should be placed where.

Leave a Reply

Top