Spring Boot CRUD Tutorial with Spring MVC, Spring Data JPA, Thymeleaf, Hibernate, MySQL

This tutorial will build a simple Student Management System web application using Spring Boot, Spring MVC, Thymeleaf, Spring Data JPA, and MySQL database.

We will build a CRUD operation for the Student entity in our Student Management System web application.

Video

Tools and Technologies Used

  • Java 17
  • Spring Boot 3
  • Spring MVC
  • Spring Data JPA ( Hibernate)
  • MySQL
  • Thymeleaf
  • Eclipse STS

1. Create Spring Boot Project

Let's open STS ( Spring Suite Tool) IDE to develop and bootstrap the Spring boot project.

Use the below guide to create a Spring boot project in Eclipse STS IDE: 

Selected below dependencies while creating spring boot project using spring initializr:
- Spring Web
- Thymeleaf
- Spring Data JPA
- MySQL Driver
- Spring Boot Devtools

2. Create Spring Boot Project Structure

Let's create the below packages in our Spring boot project:
- controller
- service
- repository
- entity

3. Maven Dependencies

Note that we are using Spring Boot 3 and it requires minimum Java version 17 or later.
		<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>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>

3. Create Student JPA Entity

Let's create a Student JPA entity under the entity package and add the following content to it:
package net.javaguides.sms.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "students")
public class Student {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(name = "first_name", nullable = false)
	private String firstName;
	
	@Column(name = "last_name")
	private String lastName;
	
	@Column(name = "email")
	private String email;
	
	public Student() {
		
	}
	
	public Student(String firstName, String lastName, String email) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
}
  • @Entity - This annotation specifies that the class is an entity. This annotation can be applied to Class, Interface of Enums.
  • @Table  - JPA annotation specifies the table in the database with which this entity is mapped.
  • @Id - The @Id JPA annotation specifies the primary key of the entity.
  • @Column - The @Column annotation is used to specify the mapping between a basic entity attribute and the database table column.

4. Create JPA StudentRepository

Let's create a StudentRepository interface under the repository package and add the following content:
package net.javaguides.sms.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import net.javaguides.sms.entity.Student;

public interface StudentRepository extends JpaRepository<Student, Long>{

}

5. Configure MySQL Database

Before configuring the MySQL database configuration in our Spring boot project, first, create a database named SMS in MySQL workbench:
create database sms
Let's open the application.properties file and add following content to it:
spring.datasource.url=jdbc:mysql://localhost:3306/sms?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=root

#Hibernate
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect

#Hibernate auto ddl
spring.jpa.hibernate.ddl-auto=update

logging.level.org.hibernate.SQL=DEBUG

6. Creating Service Layer

StudentService Interface

Let's create a StudentService interface under the service package and add the following content to it:

package net.javaguides.sms.service;

import java.util.List;

import net.javaguides.sms.entity.Student;

public interface StudentService {
	List<Student> getAllStudents();
	
	Student saveStudent(Student student);
	
	Student getStudentById(Long id);
	
	Student updateStudent(Student student);
	
	void deleteStudentById(Long id);
}

StudentServiceImpl Class

Let's create a new package called impl inside the service package. Let's create StudentServiceImpl class and add the following content to it:
package net.javaguides.sms.service.impl;

import java.util.List;

import org.springframework.stereotype.Service;

import net.javaguides.sms.entity.Student;
import net.javaguides.sms.repository.StudentRepository;
import net.javaguides.sms.service.StudentService;

@Service
public class StudentServiceImpl implements StudentService{

	private StudentRepository studentRepository;
	
	public StudentServiceImpl(StudentRepository studentRepository) {
		super();
		this.studentRepository = studentRepository;
	}

	@Override
	public List<Student> getAllStudents() {
		return studentRepository.findAll();
	}

	@Override
	public Student saveStudent(Student student) {
		return studentRepository.save(student);
	}

	@Override
	public Student getStudentById(Long id) {
		return studentRepository.findById(id).get();
	}

	@Override
	public Student updateStudent(Student student) {
		return studentRepository.save(student);
	}

	@Override
	public void deleteStudentById(Long id) {
		studentRepository.deleteById(id);	
	}
}

7. Controller Layer

Let's create a StudentController class and add the following content to it:

package net.javaguides.sms.controller;

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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import net.javaguides.sms.entity.Student;
import net.javaguides.sms.service.StudentService;

@Controller
public class StudentController {
	
	private StudentService studentService;

	public StudentController(StudentService studentService) {
		super();
		this.studentService = studentService;
	}
	
	// handler method to handle list students and return mode and view
	@GetMapping("/students")
	public String listStudents(Model model) {
		model.addAttribute("students", studentService.getAllStudents());
		return "students";
	}
	
	@GetMapping("/students/new")
	public String createStudentForm(Model model) {
		
		// create student object to hold student form data
		Student student = new Student();
		model.addAttribute("student", student);
		return "create_student";
		
	}
	
	@PostMapping("/students")
	public String saveStudent(@ModelAttribute("student") Student student) {
		studentService.saveStudent(student);
		return "redirect:/students";
	}
	
	@GetMapping("/students/edit/{id}")
	public String editStudentForm(@PathVariable Long id, Model model) {
		model.addAttribute("student", studentService.getStudentById(id));
		return "edit_student";
	}

	@PostMapping("/students/{id}")
	public String updateStudent(@PathVariable Long id,
			@ModelAttribute("student") Student student,
			Model model) {
		
		// get student from database by id
		Student existingStudent = studentService.getStudentById(id);
		existingStudent.setId(id);
		existingStudent.setFirstName(student.getFirstName());
		existingStudent.setLastName(student.getLastName());
		existingStudent.setEmail(student.getEmail());
		
		// save updated student object
		studentService.updateStudent(existingStudent);
		return "redirect:/students";		
	}
	
	// handler method to handle delete student request
	
	@GetMapping("/students/{id}")
	public String deleteStudent(@PathVariable Long id) {
		studentService.deleteStudentById(id);
		return "redirect:/students";
	}	
}

8. View Layer

resources/templates/students.html

Let's create a students.html file and add the following content to it:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Student Management System</title>
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
	integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
	crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-md bg-dark navbar-dark">
  <!-- Brand -->
  <a class="navbar-brand" href="#">Student Management System</a>

  <!-- Toggler/collapsibe Button -->
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
    <span class="navbar-toggler-icon"></span>
  </button>

  <!-- Navbar links -->
  <div class="collapse navbar-collapse" id="collapsibleNavbar">
    <ul class="navbar-nav">
      <li class="nav-item">
        <a class="nav-link" th:href="@{/students}">Student Management</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" th:href="@{/students}">Teacher Management</a>
      </li>
    </ul>
  </div>
</nav>

	<div class ="container">
		<div class = "row">
			<h1> List Students </h1>
		</div>
		
		<div class = "row">
			<div class = "col-lg-3">
				<a th:href = "@{/students/new}" class = "btn btn-primary btn-sm mb-3"> Add Student</a>
			</div>
		</div>
		<table class = "table table-striped table-bordered">
			<thead class = "table-dark">
				<tr>
					<th> Student First Name</th>
					<th> Student Last Name</th>
					<th> Student Email </th>
					<th> Actions </th>
				</tr>
			</thead>
			
			<tbody>
				<tr th:each = "student: ${students}">
					<td th:text = "${student.firstName}"></td>
					<td th:text = "${student.lastName}"></td>
					<td th:text = "${student.email}"></td>
					<td>
						<a th:href = "@{/students/edit/{id}(id=${student.id})}"
						 class = "btn btn-primary">Update</a>
						
						<a th:href = "@{/students/{id}(id=${student.id})}"
						 class = "btn btn-danger">Delete</a>
						 
					</td>
				</tr>
			</tbody>
		
		</table>
		
	</div>
</body>
</html>

We have used th:each Thymeleaf attribute in our template to iterate the list of students:

resources/templates/create_student.html

Let's create a create_student.html file and add the following content to it:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Student Management System</title>
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
	integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
	crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-md bg-dark navbar-dark">
  <!-- Brand -->
  <a class="navbar-brand" href="#">Student Management System</a>

  <!-- Toggler/collapsibe Button -->
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
    <span class="navbar-toggler-icon"></span>
  </button>

  <!-- Navbar links -->
  <div class="collapse navbar-collapse" id="collapsibleNavbar">
    <ul class="navbar-nav">
      <li class="nav-item">
        <a class="nav-link" th:href="@{/students}">Student Management</a>
      </li>
    </ul>
  </div>
</nav>
<br>
<br>
	<div class = "container">
		<div class = "row">
			<div class ="col-lg-6 col-md-6 col-sm-6 container justify-content-center card">
				<h1 class = "text-center"> Create New Student </h1>
				<div class = "card-body">
					<form th:action="@{/students}" th:object = "${student}" method="POST">
						<div class ="form-group">
							<label> Student First Name </label>
							<input
							type = "text"
							name = "firstName"
							th:field = "*{firstName}"
							class = "form-control"
							placeholder="Enter Student First Name" 
							/>
						</div>
						
						<div class ="form-group">
							<label> Student Last Name </label>
							<input
							type = "text"
							name = "lastName"
							th:field = "*{lastName}"
							class = "form-control"
							placeholder="Enter Student Last Name" 
							/>
						</div>
						
						<div class ="form-group">
							<label> Student Email </label>
							<input
							type = "text"
							name = "email"
							th:field = "*{email}"
							class = "form-control"
							placeholder="Enter Student Email" 
							/>
						</div>
						
						<div class = "box-footer">
							<button type="submit" class = "btn btn-primary">
								Submit
							</button>
						</div>
					</form>
				
				</div>
			</div>
		</div>
	</div>
</body>
</html>

resources/templates/edit_student.html

Let's create an edit_student.html file and add the following content to it:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Student Management System</title>
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
	integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
	crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-md bg-dark navbar-dark">
  <!-- Brand -->
  <a class="navbar-brand" href="#">Student Management System</a>

  <!-- Toggler/collapsibe Button -->
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
    <span class="navbar-toggler-icon"></span>
  </button>

  <!-- Navbar links -->
  <div class="collapse navbar-collapse" id="collapsibleNavbar">
    <ul class="navbar-nav">
      <li class="nav-item">
        <a class="nav-link" th:href="@{/students}">Student Management</a>
      </li>
    </ul>
  </div>
</nav>
<br>
<br>
	<div class = "container">
		<div class = "row">
			<div class ="col-lg-6 col-md-6 col-sm-6 container justify-content-center card">
				<h1 class = "text-center"> Update Student </h1>
				<div class = "card-body">
					<form th:action="@{/students/{id} (id=${student.id})}" th:object = "${student}" method="POST">
						<div class ="form-group">
							<label> Student First Name </label>
							<input
							type = "text"
							name = "firstName"
							th:field = "*{firstName}"
							class = "form-control"
							placeholder="Enter Student First Name" 
							/>
						</div>
						
						<div class ="form-group">
							<label> Student Last Name </label>
							<input
							type = "text"
							name = "lastName"
							th:field = "*{lastName}"
							class = "form-control"
							placeholder="Enter Student Last Name" 
							/>
						</div>
						
						<div class ="form-group">
							<label> Student Email </label>
							<input
							type = "text"
							name = "email"
							th:field = "*{email}"
							class = "form-control"
							placeholder="Enter Student Email" 
							/>
						</div>
						
						<div class = "box-footer">
							<button type="submit" class = "btn btn-primary">
								Submit
							</button>
						</div>
					</form>
				
				</div>
			</div>
		</div>
	</div>
</body>
</html>

9. Run Spring Boot Application

Run the Spring boot application with the main class:
package net.javaguides.sms;

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

@SpringBootApplication
public class StudentManagementSystemApplication{

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

10. Demo

Once the Spring boot application is up and running then use the below URL to access this application:

List Students

Add Student

Update Student

Comments

  1. your tutorials are helping me a lot in learning spring

    ReplyDelete
  2. Thank you for this tutorial its helping a lot. Question like if we want to add a column of marks and we want to return total number of marks in thymeleaf, how can we display that total in thymeleaf ?

    ReplyDelete

Post a Comment

Leave Comment