package com.example.studentapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class StudentappApplication {
public static void main(String[] args) {
SpringApplication.run(StudentappApplication.class, args);
}
}
---------------------------------------
package com.example.studentapp.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
public String homePage(Model model, HttpServletRequest request) {
model.addAttribute("currentUri", request.getRequestURI());
return "homefolder/index"; // No .html extension
}
}
-----------------------------------------------------
package com.example.studentapp.controller;
import com.example.studentapp.model.Student;
import com.example.studentapp.service.StudentService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@ModelAttribute("basePath")
public String getBasePath() {
return "/student";
}
// ✅ REMOVE THIS METHOD - it incorrectly maps "/student/"
// @GetMapping("/")
// public String redirectToIndex() {
// return "homefolder/index";
// }
@GetMapping({"", "/index"})
public String index(
@RequestParam(defaultValue = "0") int page,
HttpServletRequest request,
Model model) {
int pageSize = 40; // You can change to 50 in real usage
Page<Student> studentPage = studentService.getPaginatedStudents(page, pageSize);
model.addAttribute("students", studentPage.getContent());
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", studentPage.getTotalPages());
model.addAttribute("currentUri", request.getRequestURI());
return "student/index";
}
@GetMapping("/create")
public String showCreateForm(HttpServletRequest request, Model model) {
model.addAttribute("currentUri", request.getRequestURI());
model.addAttribute("student", new Student());
return "student/create";
}
@PostMapping("/create")
public String createStudent(
@Valid @ModelAttribute("student") Student student,
BindingResult bindingResult,
HttpServletRequest request,
Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("currentUri", request.getRequestURI());
return "student/create";
}
try {
studentService.saveStudent(student);
return "redirect:/student/index";
} catch (Exception e) {
model.addAttribute("error", "Failed to create student: " + e.getMessage());
model.addAttribute("currentUri", request.getRequestURI());
return "student/create";
}
}
@GetMapping("/edit/{id}")
public String showEditForm(@PathVariable Integer id, HttpServletRequest request, Model model) {
try {
Student student = studentService.getStudentById(id);
model.addAttribute("currentUri", request.getRequestURI());
model.addAttribute("student", student);
return "student/edit";
} catch (Exception e) {
model.addAttribute("error", "Student not found with ID: " + id);
return "redirect:/student/index";
}
}
@PostMapping("/edit/{id}")
public String updateStudent(@PathVariable Integer id, @Valid @ModelAttribute("student") Student student,
BindingResult bindingResult, HttpServletRequest request, Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("currentUri", request.getRequestURI());
return "student/edit";
}
try {
student.setId(id);
studentService.saveStudent(student);
return "redirect:/student/index";
} catch (Exception e) {
model.addAttribute("error", "Failed to update student: " + e.getMessage());
model.addAttribute("currentUri", request.getRequestURI());
return "student/edit";
}
}
@GetMapping("/details/{id}")
public String showDetails(@PathVariable Integer id, HttpServletRequest request, Model model) {
try {
Student student = studentService.getStudentById(id);
model.addAttribute("currentUri", request.getRequestURI());
model.addAttribute("student", student);
return "student/details";
} catch (Exception e) {
model.addAttribute("error", "Student not found with ID: " + id);
return "redirect:/student/index";
}
}
@GetMapping("/delete/{id}")
public String showDeleteConfirmation(@PathVariable Integer id, HttpServletRequest request, Model model) {
try {
Student student = studentService.getStudentById(id);
model.addAttribute("currentUri", request.getRequestURI());
model.addAttribute("student", student);
return "student/delete";
} catch (Exception e) {
model.addAttribute("error", "Student not found with ID: " + id);
return "redirect:/student/index";
}
}
@PostMapping("/delete/{id}")
public String deleteStudent(@PathVariable Integer id, Model model) {
try {
studentService.deleteStudent(id);
return "redirect:/student/index";
} catch (Exception e) {
model.addAttribute("error", "Failed to delete student: " + e.getMessage());
return "redirect:/student/index";
}
}
}
-------------------------------------------------------------------
package com.example.studentapp.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import lombok.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@Entity
@Table(name = "tblstud")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name", length = 35, nullable = false)
@NotBlank(message = "Name is required")
@Size(min = 2, max = 35, message = "Name must be between 2 and 35 characters")
@Pattern(regexp = "^[a-zA-Z ]+$", message = "Name must contain only letters and spaces")
private String name;
@Column(name = "fees", precision = 7, scale = 2, nullable = false)
@NotNull(message = "Fees is required")
@DecimalMin(value = "100.00", message = "Fees must be at least 100.00")
@DecimalMax(value = "99999.99", message = "Fees must be less than 100000.00")
@Digits(integer = 5, fraction = 2, message = "Fees must be a number with up to 5 digits and 2 decimal places")
private BigDecimal fees;
@Column(name = "mobilenumber", length = 10, nullable = false, unique = true)
@NotBlank(message = "Mobile number is required")
@Pattern(regexp = "^[0-9]{10}$", message = "Mobile number must be exactly 10 digits")
private String mobileNumber;
@Column(name = "dob", nullable = false)
@NotNull(message = "Date of birth is required")
@Past(message = "Date of birth must be in the past")
@DateTimeFormat(pattern = "yyyy-MM-dd") // Change to match form submission
private LocalDate dob;
// Utility method to return formatted DOB for UI display
public String getFormattedDob() {
if (dob != null) {
return dob.format(DateTimeFormatter.ofPattern("dd-MM-yyyy"));
}
return "";
}
}
--------------------------------------------------
StudentRepository
package com.example.studentapp.repository;
import com.example.studentapp.model.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
// JpaRepository provides CRUD operations out of the box
// First generic parameter is Entity type, second is ID type
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
----------------------------------------------------------------
StudentService
package com.example.studentapp.service;
import com.example.studentapp.model.Student;
import com.example.studentapp.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@Service
public class StudentService {
@Autowired
private StudentRepository studentRepository;
// Get all students
//// public List<Student> getAllStudents() {
//// return studentRepository.findAll();
//// }
public Page<Student> getPaginatedStudents(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return studentRepository.findAll(pageable);
}
// Save a student
public void saveStudent(Student student) {
studentRepository.save(student);
}
// Get student by ID
public Student getStudentById(Integer id) {
return studentRepository.findById(id).orElse(null);
}
// Delete student by ID
public void deleteStudent(Integer id) {
studentRepository.deleteById(id);
}
}
------------------------------------
homefolder/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title>Welcome</title>
</head>
<body>
<div layout:fragment="content">
<div class="container text-center mt-5">
<h1>Welcome </h1>
<p>This is the home page.</p>
</div>
</div>
</body>
</html>
------------------------------
student/create.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title>Create Student</title>
<!-- Add Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
</head>
<body>
<div layout:fragment="content">
<div class="container">
<h1>Create New Student</h1>
<div class="row">
<div class="col-md-8 col-lg-6">
<form th:action="@{/student/create}"
th:object="${student}"
method="post"
class="needs-validation"
novalidate>
<!-- Global error block -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i> Please fix the following errors:
<ul>
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</ul>
</div>
<!-- Error message from controller -->
<div th:if="${error}" class="alert alert-danger" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i>
<span th:text="${error}"></span>
</div>
<!-- Name field -->
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control"
th:classappend="${#fields.hasErrors('name')} ? 'is-invalid'"
id="name" th:field="*{name}" required />
<div class="invalid-feedback"
th:if="${#fields.hasErrors('name')}"
th:errors="*{name}"></div>
</div>
<!-- Fees field -->
<div class="mb-3">
<label for="fees" class="form-label">Fees</label>
<div class="input-group">
<!-- <span class="input-group-text">Rs</span>- -->
<input type="number" step="0.01" class="form-control"
th:classappend="${#fields.hasErrors('fees')} ? 'is-invalid'"
id="fees" th:field="*{fees}" required />
<div class="invalid-feedback"
th:if="${#fields.hasErrors('fees')}"
th:errors="*{fees}"></div>
</div>
</div>
<!-- Mobile number field -->
<div class="mb-3">
<label for="mobileNumber" class="form-label">Mobile Number</label>
<input type="tel" class="form-control"
th:classappend="${#fields.hasErrors('mobileNumber')} ? 'is-invalid'"
id="mobileNumber" th:field="*{mobileNumber}"
pattern="[0-9]{10}" title="10 digit mobile number" required />
<div class="invalid-feedback"
th:if="${#fields.hasErrors('mobileNumber')}"
th:errors="*{mobileNumber}"></div>
</div>
<!-- Date of birth field -->
<div class="mb-3">
<label for="dob" class="form-label">Date of Birth</label>
<input type="date" class="form-control"
th:classappend="${#fields.hasErrors('dob')} ? 'is-invalid'"
id="dob" th:field="*{dob}" required />
<div class="invalid-feedback"
th:if="${#fields.hasErrors('dob')}"
th:errors="*{dob}"></div>
</div>
<!-- Submit / Cancel buttons -->
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-4">
<button type="submit" class="btn btn-primary me-md-2">
<i class="bi bi-plus-circle"></i> Create Student
</button>
<a th:href="@{/student/index}" class="btn btn-outline-secondary">
<i class="bi bi-x-circle"></i> Cancel
</a>
<a th:href="@{/student/index}" class="btn btn-secondary">
<i class="bi bi-list-ul"></i> Back to List
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
---------------------------
student/delete.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title>Delete Student</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
</head>
<body>
<div layout:fragment="content">
<div class="container">
<h1>Delete Student</h1>
<!-- Error message from controller -->
<div th:if="${error}" class="alert alert-danger" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i>
<span th:text="${error}"></span>
</div>
<div class="alert alert-danger" role="alert">
<h3><i class="bi bi-exclamation-triangle-fill"></i> Are you sure you want to delete this student?</h3>
<p class="mb-0">This action cannot be undone.</p>
</div>
<div class="card">
<div class="card-header bg-danger text-white">
<i class="bi bi-person-x"></i> Student to be Deleted
</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-3">ID</dt>
<dd class="col-sm-9" th:text="${student.id}"></dd>
<dt class="col-sm-3">Name</dt>
<dd class="col-sm-9" th:text="${student.name}"></dd>
<dt class="col-sm-3">Fees</dt>
<dd class="col-sm-9" th:text="${#numbers.formatDecimal(student.fees, 1, 'COMMA', 2, 'POINT')}"></dd>
<dt class="col-sm-3">Mobile Number</dt>
<dd class="col-sm-9" th:text="${student.mobileNumber}"></dd>
</dl>
</div>
</div>
<form th:action="@{/student/delete/{id}(id=${student.id})}" method="post" class="mt-4">
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<button type="submit" class="btn btn-danger me-md-2">
<i class="bi bi-trash-fill"></i> Confirm Delete
</button>
<a th:href="@{/student/index}" class="btn btn-secondary">
<i class="bi bi-x-circle"></i> Cancel
</a>
</div>
</form>
</div>
</div>
</body>
</html>
-----------------------------
student/details.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title>Student Details</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
</head>
<body>
<div layout:fragment="content">
<div class="container">
<h1>Student Details</h1>
<!-- Error message from controller -->
<div th:if="${error}" class="alert alert-danger" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i>
<span th:text="${error}"></span>
</div>
<div class="card">
<div class="card-header bg-primary text-white">
<i class="bi bi-person-badge"></i> Student Information
</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-3">ID</dt>
<dd class="col-sm-9" th:text="${student.id}"></dd>
<dt class="col-sm-3">Name</dt>
<dd class="col-sm-9" th:text="${student.name}"></dd>
<dt class="col-sm-3">Fees</dt>
<dd class="col-sm-9" th:text="${#numbers.formatCurrency(student.fees)}"></dd>
<dt class="col-sm-3">Mobile Number</dt>
<dd class="col-sm-9" th:text="${student.mobileNumber}"></dd>
<dt class="col-sm-3">Date of Birth</dt>
<dd class="col-sm-9" th:text="${student.formattedDob}"></dd>
</dl>
</div>
</div>
<div class="mt-4">
<a th:href="@{/student/edit/{id}(id=${student.id})}"
class="btn btn-warning me-2">
<i class="bi bi-pencil"></i> Edit
</a>
<a th:href="@{/student/delete/{id}(id=${student.id})}"
class="btn btn-danger me-2">
<i class="bi bi-trash"></i> Delete
</a>
<a th:href="@{/student/index}" class="btn btn-secondary">
<i class="bi bi-list-ul"></i> Back to List
</a>
</div>
</div>
</div>
</body>
</html>
-------------------------------------
student/edit.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title>Edit Student</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
</head>
<body>
<div layout:fragment="content">
<div class="container">
<h1>Edit Student</h1>
<div class="row">
<div class="col-md-8 col-lg-6">
<form th:action="@{/student/edit/{id}(id=${student.id})}"
th:object="${student}"
method="post"
class="needs-validation" novalidate>
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i> Please fix the following errors:
<ul>
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</ul>
</div>
<!-- Error message from controller -->
<div th:if="${error}" class="alert alert-danger" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i>
<span th:text="${error}"></span>
</div>
<input type="hidden" th:field="*{id}" />
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control"
th:classappend="${#fields.hasErrors('name')} ? 'is-invalid'"
id="name" th:field="*{name}" required />
<div class="invalid-feedback"
th:if="${#fields.hasErrors('name')}"
th:errors="*{name}"></div>
</div>
<div class="mb-3">
<label for="fees" class="form-label">Fees</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" step="0.01" class="form-control"
th:classappend="${#fields.hasErrors('fees')} ? 'is-invalid'"
id="fees" th:field="*{fees}" required />
<div class="invalid-feedback"
th:if="${#fields.hasErrors('fees')}"
th:errors="*{fees}"></div>
</div>
</div>
<div class="mb-3">
<label for="mobileNumber" class="form-label">Mobile Number</label>
<input type="tel" class="form-control"
th:classappend="${#fields.hasErrors('mobileNumber')} ? 'is-invalid'"
id="mobileNumber" th:field="*{mobileNumber}"
pattern="[0-9]{10}" title="10 digit mobile number" required />
<div class="invalid-feedback"
th:if="${#fields.hasErrors('mobileNumber')}"
th:errors="*{mobileNumber}"></div>
</div>
<div class="mb-3">
<label for="dob" class="form-label">Date of Birth</label>
<input type="date" class="form-control"
th:classappend="${#fields.hasErrors('dob')} ? 'is-invalid'"
id="dob" th:field="*{dob}" required />
<div class="invalid-feedback"
th:if="${#fields.hasErrors('dob')}"
th:errors="*{dob}"></div>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-4">
<button type="submit" class="btn btn-primary me-md-2">
<i class="bi bi-save"></i> Save Changes
</button>
<a th:href="@{/student/index}" class="btn btn-outline-secondary">
<i class="bi bi-x-circle"></i> Cancel
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
------------------------------------------
student/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<title>Student List</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
</head>
<body>
<div layout:fragment="content">
<div class="container">
<h1>Student List</h1>
<div th:if="${error}" class="alert alert-danger" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i>
<span th:text="${error}"></span>
</div>
<p>
<a th:href="@{/student/create}" class="btn btn-primary">
<i class="bi bi-plus-circle"></i> Create New
</a>
</p>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>Fees</th>
<th>Mobile Number</th>
<th>Date of Birth</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr th:each="student : ${students}">
<td th:text="${student.id}"></td>
<td th:text="${student.name}"></td>
<td th:text="${#numbers.formatDecimal(student.fees, 1, 'COMMA', 2, 'POINT')}"></td>
<td th:text="${student.mobileNumber}"></td>
<td th:text="${student.formattedDob}"></td>
<td>
<div class="btn-group" role="group" aria-label="Student actions">
<a th:href="@{/student/details/{id}(id=${student.id})}"
class="btn btn-info btn-sm me-1">
<i class="bi bi-eye"></i> Details
</a>
<a th:href="@{/student/edit/{id}(id=${student.id})}"
class="btn btn-warning btn-sm me-1">
<i class="bi bi-pencil"></i> Edit
</a>
<a th:href="@{/student/delete/{id}(id=${student.id})}"
class="btn btn-danger btn-sm">
<i class="bi bi-trash"></i> Delete
</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- No Students Message -->
<div th:if="${#lists.isEmpty(students)}" class="alert alert-info mt-3" role="alert">
<i class="bi bi-info-circle"></i> No students found.
<a th:href="@{/student/create}">Create the first student</a>.
</div>
<!-- Pagination Controls -->
<div class="mt-4">
<nav th:if="${totalPages > 1}" aria-label="Page navigation">
<ul class="pagination justify-content-center">
<!-- Previous Button -->
<li class="page-item" th:classappend="${currentPage == 0} ? 'disabled'">
<a class="page-link" th:href="@{/student/index(page=${currentPage - 1})}" tabindex="-1">Previous</a>
</li>
<!-- Page Numbers -->
<li class="page-item"
th:each="i : ${#numbers.sequence(0, totalPages - 1)}"
th:classappend="${i == currentPage} ? 'active'">
<a class="page-link" th:href="@{/student/index(page=${i})}" th:text="${i + 1}"></a>
</li>
<!-- Next Button -->
<li class="page-item" th:classappend="${currentPage + 1 >= totalPages} ? 'disabled'">
<a class="page-link" th:href="@{/student/index(page=${currentPage + 1})}">Next</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</body>
</html>
-------------------------------------------------------
layout.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title layout:title-pattern="$CONTENT_TITLE - Student Management System">Student Management System</title>
<!-- Bootstrap & Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<!-- Custom Theme Styles -->
<style>
:root {
--bs-body-bg: #f8f9fa;
--bs-body-color: #212529;
--navbar-bg: #343a40;
--navbar-color: rgba(255, 255, 255, 0.85);
--navbar-active-bg: #495057;
--dropdown-bg: #343a40;
--dropdown-color: rgba(255, 255, 255, 0.85);
--validation-error: #dc3545;
--content-bg: #ffffff;
}
[data-bs-theme="dark"] {
--bs-body-bg: #212529;
--bs-body-color: #f8f9fa;
--navbar-bg: #1a1a1a;
--navbar-color: rgba(255, 255, 255, 0.85);
--navbar-active-bg: #2c3034;
--dropdown-bg: #2c3034;
--dropdown-color: rgba(255, 255, 255, 0.85);
--validation-error: #ff6b6b;
--content-bg: #2c3034;
}
body {
background-color: var(--bs-body-bg);
color: var(--bs-body-color);
padding-top: 56px;
min-height: 100vh;
}
.navbar-custom {
background-color: var(--navbar-bg) !important;
color: var(--navbar-color);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.navbar-custom .navbar-brand,
.navbar-custom .nav-link {
color: var(--navbar-color);
}
.navbar-custom .nav-link:hover,
.navbar-custom .nav-link:focus,
.navbar-custom .nav-link.active {
background-color: var(--navbar-active-bg);
color: white;
}
.content-container {
padding: 2rem;
background-color: var(--content-bg);
border-radius: 0.5rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
margin-top: 1rem;
}
.card {
background-color: var(--content-bg);
border-color: rgba(0, 0, 0, 0.125);
}
.validation-summary-errors,
.field-validation-error {
color: var(--validation-error);
}
.input-validation-error {
border-color: var(--validation-error) !important;
}
.table {
background-color: var(--content-bg);
}
.table th {
background-color: var(--navbar-bg);
color: var(--navbar-color);
}
.form-control, .form-select {
background-color: var(--content-bg);
color: var(--bs-body-color);
border-color: #ced4da;
}
.footer {
background-color: var(--navbar-bg);
color: var(--navbar-color);
padding: 1rem 0;
margin-top: 2rem;
}
</style>
</head>
<body>
<!-- Top Navbar -->
<nav class="navbar navbar-expand-md navbar-custom fixed-top">
<div class="container">
<a class="navbar-brand" th:href="@{/student/index}">
<i class="bi bi-people-fill me-2"></i>Yourwebsite logo
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavbar"
aria-controls="mainNavbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" th:href="@{/}"
th:classappend="${#strings.contains(currentUri, '/homefolder/index') or currentUri == '/'} ? 'active'">
<i class="bi bi-house-door me-1"></i>Home
</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/student/create}"
th:classappend="${#strings.contains(currentUri, '/student/create')} ? 'active'">
<i class="bi bi-person-plus me-1"></i>Add Student
</a>
</li>
</ul>
<!-- Theme Switch -->
<div class="d-flex">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="darkModeSwitch">
<label class="form-check-label" for="darkModeSwitch">
<i class="bi" id="themeIcon"></i>
<span id="themeText">Dark Mode</span>
</label>
</div>
</div>
</div>
</div>
</nav>
<!-- Main Content Area -->
<main class="container my-4">
<div th:if="${error}" class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
<span th:text="${error}"></span>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<!-- Removed form-level validation block to prevent Thymeleaf error -->
<div layout:fragment="content" class="content-container">
<!-- Content will be inserted here -->
</div>
</main>
<footer class="footer mt-auto">
<div class="container text-center">
<span class="text-muted">© 2025 Student Management System</span>
</div>
</footer>
<!-- JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const themeSwitch = document.getElementById('darkModeSwitch');
const themeIcon = document.getElementById('themeIcon');
const themeText = document.getElementById('themeText');
const savedTheme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
setTheme(savedTheme);
themeSwitch.addEventListener('change', function() {
const newTheme = this.checked ? 'dark' : 'light';
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
});
function setTheme(theme) {
document.documentElement.setAttribute('data-bs-theme', theme);
themeSwitch.checked = theme === 'dark';
themeIcon.className = theme === 'dark' ? 'bi bi-sun-fill' : 'bi bi-moon-fill';
themeText.textContent = theme === 'dark' ? 'Light Mode' : 'Dark Mode';
}
(function () {
'use strict'
const forms = document.querySelectorAll('.needs-validation')
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
});
</script>
</body>
</html>
------------------------------------------
application.properties
spring.application.name=studentapp
# Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/dbstud?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=1804
# Hibernate properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.generate-ddl=true
spring.jpa.defer-datasource-initialization=true
# Thymeleaf configuration
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.enabled=true
server.port=8080
server.error.include-message=always
server.error.include-stacktrace=always
#indian rs currency sign
spring.mvc.locale=hi_IN
spring.mvc.locale-resolver=fixed
----------------------------------------------------
pom.xml
<?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
https://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>3.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>studentapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>studentapp</name>
<description>dbstudapp for demo</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<!-- Web MVC Support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf Templating Engine -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Thymeleaf layout page menubar -->
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>3.2.1</version> <!-- or the latest version -->
</dependency>
<!-- Spring Data JPA with Hibernate -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Form Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Development Tools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Optional: Lombok for reducing boilerplate code -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
pagination also added.