DEV Community

Cover image for Building a Student Management API with Spring Boot: A Step-by-Step Guide
Mohammed Quasim D A
Mohammed Quasim D A

Posted on

Building a Student Management API with Spring Boot: A Step-by-Step Guide

github:https://github.com/mohammedQuasimDA/smart-campus
If you:

    Have doubts about any part of this guide
    Find something confusing or unclear
    Notice typos/errors
Enter fullscreen mode Exit fullscreen mode

Please reach out in the comments/DMsβ€”I’d be delighted to:

    Clarify concepts
    Fix mistakes promptly
    Improve this resource for everyone
Enter fullscreen mode Exit fullscreen mode

Your feedback helps make this article more accurate and helpful for future readers!

This project is a Student Management System built as a RESTful API using Spring Boot. It allows you to perform CRUD (Create, Read, Update, Delete) operations on student records, such as:

  1. Adding new students
  2. Retrieving student details
  3. Updating student information
  4. Deleting student dent records

πŸš€ Getting Started
go to https://start.spring.io/ or search "spring initializr" in google


- project:maven
- language:java
- Spring boot:3.4.5 (or any version of your choice)
- Group: this is basically your company name in reverse order. like google.com-->com.google  if you are individual you can just com.your_name
- Artifact: give your project an artifact name, e.g., β€œdemo.”
- Name: enter a name for your project, e.g., β€œSpringBootDemo.”
- Description: this field is optional.
- Package name: define your base package automatically generated you can also change it but it is combo of group and artifact , e.g., β€œcom.learning.demo.”
- Packaging: choose β€œJar” for a standalone JAR file.
- java:21
Enter fullscreen mode Exit fullscreen mode
Dependencies:
1.     Spring Web
2.     Spring data JPA
3.     Mysql driver

Enter fullscreen mode Exit fullscreen mode

Now click on Generate which will generate a zip ,extract it and open in IDE

My project name is smartcampus so it creates an folder with the same name
inside it you will find "src" folder under it "main" under it "java"

-->smartcampus
  |->src
    |->main
      |->java
         |->com.sc.smartcampus
      |->resources
Enter fullscreen mode Exit fullscreen mode

1. Understanding the Model Layer

The Model layer represents the structure of data in your application. Think of it like blueprints for the data you store in the database.

Below is the folder struct we gonna create

-->smartcampus
  |->src
    |->main
      |->java
         |->com.sc.smartcampus
           |->model
               |->Student.java
               |->Department(enum)
           |->converter
               |->DepartmentConverter.java
      |->resources

Enter fullscreen mode Exit fullscreen mode
package com.sc.smartcampus.model;

import java.time.LocalDate;

import com.sc.smartcampus.converter.DepartmentConverter;
import com.sc.smartcampus.validation.ValidRollNumber;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;

@Entity
@Table(name = "students",indexes = @Index(columnList = "rollNumber",unique = true))
public class Student {

    @Id
    @Column(unique = true, length = 7) // Set to maximum possible length
    @ValidRollNumber
    private String rollNumber;

    @NotBlank(message = "Name is mandatory")
    @Size(min = 2, max = 50, message = "Name must be 2-50 characters")
    private String name;

    @Email(message = "Invalid email format")
    @Column(unique = true)
    private String email;

    @Column(name = "department")
    @Convert(converter = DepartmentConverter.class)
    private Department department;

    @NotNull(message = "Enrollment date is required")
    private LocalDate enrollmentDate;
    //getter setter , all args and no args construtor

}
Enter fullscreen mode Exit fullscreen mode

β€’ @Entity: Tells Spring Boot this class represents a database table.
β€’ name = "students": Sets the name of the table in the database to students.
β€’ indexes = @Index(...): Creates a database index on the rollNumber column to speed up search queries and enforce uniqueness.
β€’ unique = true: Ensures that no two students can have the same roll number.
β€’ @id: Marks this field as the primary key (unique identifier).
β€’ @column: Configures the database column.
β€’ unique = true: Ensures no two rows can have the same value.
β€’ length = 7: Limits the max characters to 7 (for example, 22BCS01).
β€’ @ValidRollNumber:custom validation annotation // will show
β€’ @NotBlankEnsures that the field is not null or empty
β€’ @Size:Ensures that the name is between 2 and 50 characters long
β€’ @Email:Makes sure the email field is a valid email address (like [email protected])
β€’ @column(name = "department"): Creates a column named "department"
β€’ @Convert(...): Uses a custom converter (here, DepartmentConverter) to convert the Department enum to a code like "CS" and vice versa.//will show

package com.sc.smartcampus.model;

public enum Department {
    // Core Engineering
    AEROSPACE("AE", "Aerospace Engineering"),
    AUTOMOBILE("AU", "Automobile Engineering"),
    BIOTECH("BT", "Biotechnology"),
    CHEMICAL("CH", "Chemical Engineering"),
    CIVIL("CE", "Civil Engineering"),
    COMPUTER_SCIENCE("CS", "Computer Science"),
    COMPUTER_ENGINEERING("CSE", "Computer Science Engineering"),
    ELECTRICAL("EE", "Electrical Engineering"),
    ELECTRONICS("EC", "Electronics Engineering"),
    ELECTRONICS_COMMUNICATION("ECE", "Electronics and Communication"),
    ENVIRONMENTAL("EN", "Environmental Engineering"),
    INDUSTRIAL("IE", "Industrial Engineering"),
    INFORMATION_TECH("IT", "Information Technology"),
    INSTRUMENTATION("IC", "Instrumentation and Control"),
    MECHANICAL("ME", "Mechanical Engineering"),
    MECHATRONICS("MT", "Mechatronics"),
    METALLURGY("ML", "Metallurgical Engineering"),
    MINING("MN", "Mining Engineering"),
    PETROLEUM("PE", "Petroleum Engineering"),
    PRODUCTION("PD", "Production Engineering"),
    ROBOTICS("RB", "Robotics Engineering"),
    TEXTILE("TE", "Textile Engineering"),
    ARTIFICIAL_INTELLIGENCE("AI", "Artificial Intelligence"),
    DATA_SCIENCE("DS", "Data Science"),
    CYBER_SECURITY("CY", "Cyber Security"),
    CLOUD_COMPUTING("CC", "Cloud Computing"),
    IOT("IO", "Internet of Things");

    private final String code;
    private final String fullName;

    Department(String code, String fullName) {
        this.code = code;
        this.fullName = fullName;
    }

    public String getCode() {
        return code;
    }

    public String getFullName() {
        return fullName;
    }

    // Helper method to get department from code
    public static Department fromCode(String code) {
        for (Department dept : values()) {
            if (dept.code.equalsIgnoreCase(code)) {
                return dept;
            }
        }
        throw new IllegalArgumentException("Invalid department code: " + code);
    }

}
Enter fullscreen mode Exit fullscreen mode

This class is a Java enum (enumeration) called Department, and it's used to define a fixed list of engineering departments along with a short code and full name for each one

Purpose of Department enum

This enum helps you:
1. Store departments using short codes like "CS" or "ME" in the database.
2. Show full names like "Computer Science" or "Mechanical Engineering" in the UI.

β€’ code: Short version like "CS" or "ME".
β€’ fullName: Full readable name like "Computer Science"

fromCode() Method convert a code (like "ME") back into an MECHANICAL:

package com.sc.smartcampus.converter;

import com.sc.smartcampus.model.Department;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

@Converter(autoApply = true)
public class DepartmentConverter implements AttributeConverter<Department, String> {

    @Override
    public String convertToDatabaseColumn(Department department) {
        if (department == null) return null;
        return department.getCode(); // store "ME" instead of "MECHANICAL"
    }

    @Override
    public Department convertToEntityAttribute(String code) {
        if (code == null) return null;
        return Department.fromCode(code); // convert "ME" back to MECHANICAL
    }
}
Enter fullscreen mode Exit fullscreen mode

Converter

Problem It Solves

By default, if you use an enum in a JPA entity (like Department department in Student), it will store it in the database either as:
β€’ The ordinal (position number, like 0, 1, 2) β€” not readable.
β€’ Or the enum name (like "MECHANICAL").
But you want to store the short code instead, like "ME" or "CS". Why?
βœ… It's compact
βœ… It's readable
βœ… It's customized to your preference ("CS" instead of "COMPUTER_SCIENCE")
To do this, we use a custom converter with JPA: DepartmentConverter.

@Converter(autoApply = true)
Automatically use this converter for any Department type in your entity classes

AttributeConverter
will convert between the Department enum (Java side) and a String (DB side)

validation

-->smartcampus
  |->src
    |->main
      |->java
         |->com.sc.smartcampus
           |->model
           |->converter
           |->validation
                   |->RollNumberValidator.java
                   |->ValidRollNumber(@interface)
      |->resources
Enter fullscreen mode Exit fullscreen mode

In our model we have @ValidRollNumber for private String rollNumber; which was a custome
What We're Doing Here
We're creating a custom validation annotation that checks if a roll number:
1. Follows the correct pattern like 23CS001 (2-digit year + department code + 3 digits)
2. Has a valid department code like CS, ME, EC, etc.

@Documented
@Constraint(validatedBy = RollNumberValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidRollNumber {
    String message() default "Invalid roll number or department code";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
Enter fullscreen mode Exit fullscreen mode

β€’ @Constraint: This tells Spring which validator class will handle this annotation (in our case, RollNumberValidator).
β€’ @Target(ElementType.FIELD): Can be used on fields.
β€’ @Retention(RUNTIME): The annotation should be available at runtime.

String message() default "Invalid roll number or department code";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};

are required parts of every custom validation annotation in Spring Boot (using Jakarta Bean Validation).

package com.sc.smartcampus.validation;

import com.sc.smartcampus.model.Department;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class RollNumberValidator implements ConstraintValidator<ValidRollNumber, String> {

    @Override
    public boolean isValid(String rollNumber, ConstraintValidatorContext context) {
        if (rollNumber == null || !rollNumber.matches("^\\d{2}[A-Z]{1,2}\\d{3}$")) {
            return false; // format is invalid
        }

        // extract department code
        String deptCode = rollNumber.substring(2, rollNumber.length() - 3);
        try {
            Department.fromCode(deptCode); // checks if dept code is valid
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

public class RollNumberValidator implements ConstraintValidator-->
β€’ This is a custom validator class for the annotation @ValidRollNumber.
β€’ It tells Spring how to validate a String field (like a roll number).
β€’ ConstraintValidator: Here, A = ValidRollNumber (custom annotation), and T = String (type of the field being validated).
rollNumber.substring(2, rollNumber.length() - 3);-->it takes only the code from the pattern 21ME093-->ME

2. Repository Layer

In Spring Boot, the repository layer is used to interact with the database. It contains the code to save, find, delete, or update data.
Spring Data JPA makes this super simple β€” you don’t need to write SQL for common tasks!

-->smartcampus
  |->src
    |->main
      |->java
         |->com.sc.smartcampus
           |->model
           |->converter
           |->validation
               |->repository
                   |-> StudentRepository  
      |->resources
Enter fullscreen mode Exit fullscreen mode
package com.sc.smartcampus.repository;

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

import com.sc.smartcampus.model.Student;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface StudentRepository extends JpaRepository<Student, String> {

    @Query("SELECT MAX(s.rollNumber) FROM Student s WHERE s.rollNumber LIKE :pattern")
    String findMaxRollNumberByPattern(@Param("pattern") String pattern);

    @Query("SELECT s FROM Student s WHERE LOWER(s.name) LIKE LOWER(CONCAT('%', :keyword, '%')) " +
            "OR LOWER(s.email) LIKE LOWER(CONCAT('%', :keyword, '%')) " +
            "OR LOWER(s.rollNumber) LIKE LOWER(CONCAT('%', :keyword, '%'))")
    List<Student> searchByKeyword(@Param("keyword") String keyword);
}

Enter fullscreen mode Exit fullscreen mode

-->The

here JPA give you many querys like findById() etc.

but sometime we need custom queries like to get maximum roll number
@Query("SELECT MAX(s.rollNumber) FROM Student s WHERE s.rollNumber LIKE :pattern")
String findMaxRollNumberByPattern(@Param("pattern") String pattern);

String pattern -->it wil have a pattern[17ME204] which user passes
so what we do is that we search the DB with the same pattern and find the highest roll number.
When adding a new student, you might want to auto-generate the next roll number based on a pattern

@Query("SELECT s FROM Student s WHERE LOWER(s.name) LIKE LOWER(CONCAT('%', :keyword, '%')) " +
"OR LOWER(s.email) LIKE LOWER(CONCAT('%', :keyword, '%')) " +
"OR LOWER(s.rollNumber) LIKE LOWER(CONCAT('%', :keyword, '%'))")
List<Student> searchByKeyword(@Param("keyword") String keyword);

Allows you to search students by name, email, or roll number β€” case-insensitive and partial match

3. DTOs (Data Transfer Objects)

A DTO (Data Transfer Object) is a simple Java class used to transfer data between the frontend and backend.
We use DTOs to:
β€’ Control what data is exposed to clients.
β€’ Separate internal entity logic from external data exchange.
β€’ Apply input validation rules cleanly.

-->smartcampus
  |->src
    |->main
      |->java
         |->com.sc.smartcampus
           |->model
           |->converter
           |->validation
               |->repository
               |->dto
                   |-> StudentRequestDTO
                   |-> StudentResponseDTO  
      |->resources
Enter fullscreen mode Exit fullscreen mode

StudentRequestDTO – When client sends data to backend (POST/PUT)

package com.sc.smartcampus.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

import java.time.LocalDate;

public class StudentRequestDTO {

    @NotBlank(message = "Name is required")
    @Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
    private String name;

    @Email(message = "Invalid email format")
    @NotBlank(message = "Email is required")
    private String email;

    @NotBlank(message = "Department code is required")
    private String department;

    @NotNull(message = "Enrollment date is required")
    private LocalDate enrollmentDate;

    public StudentRequestDTO() {
    }

    public StudentRequestDTO(String name, String email, String department, LocalDate enrollmentDate) {
        this.name = name;
        this.email = email;
        this.department = department;
        this.enrollmentDate = enrollmentDate;
    }

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public LocalDate getEnrollmentDate() {
        return enrollmentDate;
    }

    public void setEnrollmentDate(LocalDate enrollmentDate) {
        this.enrollmentDate = enrollmentDate;
    }
}
Enter fullscreen mode Exit fullscreen mode

β€’ @notblank: Field cannot be empty or only spaces.
β€’ @Size(min, max): Limits name length.

StudentResponseDTO

When backend sends data to client (GET)
Purpose:
This DTO is used when the backend sends student data to the frontend. It contains:
β€’ The generated roll number
β€’ Both department code (CS) and full name (Computer Science)
β€’ All other student info

package com.sc.smartcampus.dto;

import java.time.LocalDate;

public class StudentResponseDTO {

    private String rollNumber;
    private String name;
    private String email;
    private String department;          // e.g., "CS"
    private String departmentFullName;  // e.g., "Computer Science"
    private LocalDate enrollmentDate;

    public StudentResponseDTO() {
    }

    public StudentResponseDTO(String rollNumber, String name, String email,
                              String department, String departmentFullName,
                              LocalDate enrollmentDate) {
        this.rollNumber = rollNumber;
        this.name = name;
        this.email = email;
        this.department = department;
        this.departmentFullName = departmentFullName;
        this.enrollmentDate = enrollmentDate;
    }

    public String getRollNumber() {
        return rollNumber;
    }

    public void setRollNumber(String rollNumber) {
        this.rollNumber = rollNumber;
    }

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public String getDepartmentFullName() {
        return departmentFullName;
    }

    public void setDepartmentFullName(String departmentFullName) {
        this.departmentFullName = departmentFullName;
    }

    public LocalDate getEnrollmentDate() {
        return enrollmentDate;
    }

    public void setEnrollmentDate(LocalDate enrollmentDate) {
        this.enrollmentDate = enrollmentDate;
    }
}
Enter fullscreen mode Exit fullscreen mode

Image description

Service

What is a Service Interface?

In Spring Boot, the service layer is where:
β€’ Core business logic is written
β€’ The controller sends requests here
β€’ The repository is called from here to talk to the database

-->smartcampus
  |->src
    |->main
      |->java
         |->com.sc.smartcampus
           |->model
           |->converter
           |->validation
               |->repository
               |->dto
               |->service
                   |->impl
                        |->StudentServiceImpl.java
                   |->StudentService(interface) 
      |->resources
Enter fullscreen mode Exit fullscreen mode
package com.sc.smartcampus.service;

import com.sc.smartcampus.dto.StudentRequestDTO;
import com.sc.smartcampus.dto.StudentResponseDTO;

import java.util.List;

public interface StudentService {

    StudentResponseDTO createStudent(StudentRequestDTO dto);

    StudentResponseDTO getStudentByRollNumber(String rollNumber);

    StudentResponseDTO updateStudent(String rollNumber, StudentRequestDTO dto);

    void deleteStudent(String rollNumber);

    List<StudentResponseDTO> getAllStudents();

    List<StudentResponseDTO> searchStudents(String keyword);

    String generateNextRollNumber(String year, String departmentCode);
}


Enter fullscreen mode Exit fullscreen mode

What Is StudentServiceImpl?

β€’ It implements the methods defined in your StudentService interface.
β€’ Handles all business logic: creating, updating, deleting, searching, and managing roll numbers.
β€’ Talks to the repository (StudentRepository) to interact with the database.
β€’ Converts data between the DTOs and Entity classes.
Enter fullscreen mode Exit fullscreen mode
private StudentRepository studentRepository;

public StudentServiceImpl(StudentRepository studentRepository) {
    this.studentRepository = studentRepository;
}
Enter fullscreen mode Exit fullscreen mode

To use/access the repository


package com.sc.smartcampus.service.impl;

import com.sc.smartcampus.dto.StudentRequestDTO;
import com.sc.smartcampus.dto.StudentResponseDTO;
import com.sc.smartcampus.model.Department;
import com.sc.smartcampus.model.Student;
import com.sc.smartcampus.repository.StudentRepository;
import com.sc.smartcampus.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class StudentServiceImpl implements StudentService {


    private StudentRepository studentRepository;

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

    private StudentResponseDTO mapToResponseDTO(Student student) {
        StudentResponseDTO dto = new StudentResponseDTO();
        dto.setRollNumber(student.getRollNumber());
        dto.setName(student.getName());
        dto.setEmail(student.getEmail());
        dto.setDepartment(student.getDepartment().getCode());
        dto.setDepartmentFullName(student.getDepartment().getFullName());
        dto.setEnrollmentDate(student.getEnrollmentDate());
        return dto;
    }


// Implement all methods here...

    @Override
    public StudentResponseDTO createStudent(StudentRequestDTO dto) {
        // 1. Convert department code string to enum (throws exception if invalid)
        Department department = Department.fromCode(dto.getDepartment());

        // 2. Generate the year part from enrollment date
        String year = String.valueOf(dto.getEnrollmentDate().getYear()).substring(2); // last two digits, e.g. "23"

        // 3. Generate the next roll number for this year + department
        String nextRollNumber = generateNextRollNumber(year, department.getCode());

        // 4. Create new Student entity
        Student student = new Student();
        student.setRollNumber(nextRollNumber);
        student.setName(dto.getName());
        student.setEmail(dto.getEmail());
        student.setDepartment(department);
        student.setEnrollmentDate(dto.getEnrollmentDate());

        // 5. Save student entity
        student = studentRepository.save(student);

        // 6. Convert to response DTO and return
        return mapToResponseDTO(student);
    }



    @Override
    public StudentResponseDTO getStudentByRollNumber(String rollNumber) {
        Student student = studentRepository.findById(rollNumber)
                .orElseThrow(() -> new RuntimeException("Student not found with roll number: " + rollNumber));
        return mapToResponseDTO(student);
    }


    @Override
    public StudentResponseDTO updateStudent(String rollNumber, StudentRequestDTO dto) {
        // 1. Fetch the existing student by rollNumber
        Student student = studentRepository.findById(rollNumber)
                .orElseThrow(() -> new RuntimeException("Student not found with roll number: " + rollNumber));

        // 2. Update the fields from the DTO (except rollNumber, which is immutable)
        student.setName(dto.getName());
        student.setEmail(dto.getEmail());

        // 3. Convert and update department (validate department code)
        Department department = Department.fromCode(dto.getDepartment());
        student.setDepartment(department);

        // 4. Update enrollment date
        student.setEnrollmentDate(dto.getEnrollmentDate());

        // 5. Save the updated student
        student = studentRepository.save(student);

        // 6. Convert to response DTO and return
        return mapToResponseDTO(student);
    }


    @Override
    public void deleteStudent(String rollNumber) {
        // Check if student exists, else throw exception
        Student student = studentRepository.findById(rollNumber)
                .orElseThrow(() -> new RuntimeException("Student not found with roll number: " + rollNumber));

        // Delete the student
        studentRepository.delete(student);
    }


    @Override
    public List<StudentResponseDTO> getAllStudents() {
        List<Student> students = studentRepository.findAll();
        return students.stream()
                .map(this::mapToResponseDTO)
                .collect(Collectors.toList());
    }


    @Override
    public List<StudentResponseDTO> searchStudents(String keyword) {
        List<Student> students = studentRepository.searchByKeyword(keyword);
        return students.stream()
                .map(this::mapToResponseDTO)
                .collect(Collectors.toList());
    }


    @Override
    public String generateNextRollNumber(String year, String departmentCode) {
        // 1. Create the pattern to find max existing roll numbers for the year+dept
        // Example pattern: "21CS%" to match roll numbers starting with 21CS
        String pattern = year + departmentCode + "%";

        // 2. Get the max existing roll number with this pattern from DB
        String maxRollNumber = studentRepository.findMaxRollNumberByPattern(pattern);

        int nextSerial = 1; // default if no existing roll numbers

        if (maxRollNumber != null) {
            // Extract the last 3 digits of the roll number (serial number)
            String serialStr = maxRollNumber.substring(maxRollNumber.length() - 3);
            try {
                int serialNum = Integer.parseInt(serialStr);
                nextSerial = serialNum + 1;
            } catch (NumberFormatException e) {
                // fallback if parsing fails, start from 1
                nextSerial = 1;
            }
        }

        // 3. Format the serial to be 3 digits with leading zeros (e.g., 001, 042)
        String serialFormatted = String.format("%03d", nextSerial);

        // 4. Build and return the next roll number string
        return year + departmentCode + serialFormatted;
    }


    // You can add private helper methods if needed, for example, a mapper from Student to DTO
}


Enter fullscreen mode Exit fullscreen mode

createStudent(StudentRequestDTO dto)

Converts StudentRequestDTO β†’ Student β†’ StudentResponseDTO
Steps:
1. Converts department string to a valid Department enum using Department.fromCode().
2. Extracts year from enrollment date (e.g., 2023 β†’ "23").
3. Generates the next available roll number like 23CS001.
4. Creates and saves a Student entity.
5. Converts entity to response DTO.

getStudentByRollNumber(String rollNumber)

β€’  Finds a student using studentRepository.findById(...).
β€’  Throws a RuntimeException if not found.
β€’  Returns a StudentResponseDTO
Enter fullscreen mode Exit fullscreen mode

updateStudent(String rollNumber, StudentRequestDTO dto)

Steps:
1. Fetch existing student.
2. Update fields except roll number.
3. Validate department again using fromCode.
4. Save and return updated StudentResponseDTO

deleteStudent(String rollNumber)

β€’  Checks if student exists.
β€’  Deletes using studentRepository.delete()
Enter fullscreen mode Exit fullscreen mode

getAllStudents()

β€’  Fetches all students.
β€’ Maps each entity to a StudentResponseDTO
Enter fullscreen mode Exit fullscreen mode

searchStudents(String keyword)

β€’ Uses a custom query to search by name, email, or roll number.
β€’  Converts results to DTOs.
Enter fullscreen mode Exit fullscreen mode

generateNextRollNumber(String year, String departmentCode)

Logic:
1. Constructs a search pattern like 23CS%.
2. Finds the current highest roll number matching the pattern.
3. Extracts the last 3 digits β†’ increments β†’ formats to 3 digits (e.g., 003).
4. Returns the new roll number: e.g., 23CS004

mapToResponseDTO

β€’ Converts a Student entity into a readable API-friendly response object (StudentResponseDTO).
β€’ Useful to prevent exposing internal entity structure.
Enter fullscreen mode Exit fullscreen mode

Controller

-->smartcampus
  |->src
    |->main
      |->java
         |->com.sc.smartcampus
           |->model
           |->converter
           |->validation
               |->repository
               |->dto
               |->service
               |-controller
                   |->StudentController 
      |->resources
Enter fullscreen mode Exit fullscreen mode

Purpose of StudentController

This class handles all HTTP requests related to the Student resource. It's part of the Controller layer in a Spring Boot application, which connects the client (frontend/API consumer) to the Service layer.
You’re using:
β€’ @RestController – to mark this class as a REST controller.
@RequestMapping("/api/students") – so all routes start with /api/students

package com.sc.smartcampus.controller;

import com.sc.smartcampus.dto.StudentRequestDTO;
import com.sc.smartcampus.dto.StudentResponseDTO;
import com.sc.smartcampus.service.StudentService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/students")
public class StudentController {

    @Autowired
    private StudentService studentService;

    // 1. Create Student
    @PostMapping
    public ResponseEntity<StudentResponseDTO> createStudent(@Valid @RequestBody StudentRequestDTO dto) {
        StudentResponseDTO created = studentService.createStudent(dto);
        return ResponseEntity.ok(created);
    }

    // 2. Get Student by Roll Number
    @GetMapping("/{rollNumber}")
    public ResponseEntity<StudentResponseDTO> getStudentByRollNumber(@PathVariable String rollNumber) {
        StudentResponseDTO student = studentService.getStudentByRollNumber(rollNumber);
        return ResponseEntity.ok(student);
    }

    // 3. Update Student by Roll Number
    @PutMapping("/{rollNumber}")
    public ResponseEntity<StudentResponseDTO> updateStudent(@PathVariable String rollNumber,
                                                            @Valid @RequestBody StudentRequestDTO dto) {
        StudentResponseDTO updated = studentService.updateStudent(rollNumber, dto);
        return ResponseEntity.ok(updated);
    }

    // 4. Delete Student by Roll Number
    @DeleteMapping("/{rollNumber}")
    public ResponseEntity<Void> deleteStudent(@PathVariable String rollNumber) {
        studentService.deleteStudent(rollNumber);
        return ResponseEntity.noContent().build();
    }

    // 5. Get All Students
    @GetMapping
    public ResponseEntity<List<StudentResponseDTO>> getAllStudents() {
        List<StudentResponseDTO> students = studentService.getAllStudents();
        return ResponseEntity.ok(students);
    }

    // 6. Search Students by keyword
    @GetMapping("/search")
    public ResponseEntity<List<StudentResponseDTO>> searchStudents(@RequestParam String keyword) {
        List<StudentResponseDTO> results = studentService.searchStudents(keyword);
        return ResponseEntity.ok(results);
    }
}
Enter fullscreen mode Exit fullscreen mode

β€’ @PostMapping – handles POST /api/students.
β€’ @RequestBody – reads JSON body into a Java object.
β€’ @valid – triggers validation (e.g., @notblank, @ValidRollNumber).
β€’ Returns ResponseEntity.ok(...) with the saved student data
β€’ @GetMapping("/{rollNumber}") – dynamic path parameter.
β€’ @PathVariable – binds the rollNumber from the URL
β€’ @PutMapping("/{rollNumber}")-used to update a full resource
β€’ @DeleteMapping("/{rollNumber}")-delte a resource
β€’ @RequestParam is used for query parameters like /api/students/search?keyword=john

Connect to Database

-->smartcampus
  |->src
    |->main
      |->java
         |->com.sc.smartcampus
           |->model
           |->converter
           |->validation
               |->repository
               |->dto
               |->service
               |-controller 
      |->resources
               |->application.properties
Enter fullscreen mode Exit fullscreen mode
spring.datasource.url=jdbc:mysql://localhost:3306/database_name
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

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

server.port=9090--> i used port 9090 by default it is 8080
Enter fullscreen mode Exit fullscreen mode

We have create the spring boot app now run and pass api via postman
if you are using default port change 9090 to 8080

POST http://localhost:9090/api/students
Content-Type: application/json

{
  "name": "Joqsadhn a Doe",
  "email": "[email protected]",
  "department": "CS",
  "enrollmentDate": "2023-08-15"
}
Enter fullscreen mode Exit fullscreen mode
GET http://localhost:9090/api/students
Enter fullscreen mode Exit fullscreen mode
GET http://localhost:9090/api/students/23CS001
Enter fullscreen mode Exit fullscreen mode

DELETE http://localhost:9090/api/students/21CS001
Enter fullscreen mode Exit fullscreen mode
GET http://localhost:9090/api/students/search?keyword=john
Enter fullscreen mode Exit fullscreen mode
PUT http://localhost:9090/api/students/23CS001
Content-Type: application/json

{
"name": "John Doe Updated",
"email": "[email protected]",
"department": "ME",
"enrollmentDate": "2023-08-20"
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)