Tuesday, 5 August 2025

spring boot crud jdk 17 with lombok hibernet mysql thymeleaf

 



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.