dgPadBootcamps / Java-Bootcamp-2024

1 stars 0 forks source link

Task 4: Spring boot with database #196

Open mohammad-fahs opened 2 months ago

mohammad-fahs commented 2 months ago

Task: Create an Advanced Student Management System Using Spring Boot, MySQL Database, and REST APIs

Objective:

Your task is to create an advanced Student Management System using Spring Boot, MySQL, and RESTful APIs. This system will manage not only students but also courses and enrollments. You will create a relational database structure that includes multiple entities and relationships between them. This exercise will help you understand how to build a more complex CRUD application with multiple entities and relationships using Spring Boot, JPA, and MySQL.

user this discussion for help:

https://github.com/dgPadBootcamps/Java-Bootcamp-2024/discussions/195

Make Sure to use the best practice architecture discussed in the latest sessions

Requirements:

  1. Project Setup:
    • Set up a Spring Boot project using Maven.
    • Include dependencies for Spring Web, Spring Data JPA, MySQL Connector, and any other libraries you may find necessary.
  2. Database Setup:
    • Install MySQL or use an existing MySQL server.
    • Create a new database named student_management.
    • In your application.properties (or application.yml) file, configure the connection to the MySQL database, including the database URL, username, and password.
  3. Entity Classes:

    • Create the following entities with appropriate JPA annotations:

    a. Student:

    • Fields:
      • id: Primary key, auto-generated.
      • firstName: String, not null.
      • lastName: String, not null.
      • email: String, not null, unique.
      • age: Integer.
      • enrollments: One-to-Many relationship with the Enrollment entity.

    b. Course:

    • Fields:
      • id: Primary key, auto-generated.
      • courseName: String, not null.
      • courseCode: String, not null, unique.
      • credits: Integer.
      • instructor: String, not null.
      • enrollments: One-to-Many relationship with the Enrollment entity.

    c. Enrollment:

    • Fields:
      • id: Primary key, auto-generated.
      • student: Many-to-One relationship with the Student entity.
      • course: Many-to-One relationship with the Course entity.
      • enrollmentDate: Date of enrollment.
      • grade: String, grade received in the course.
    • Ensure that each entity class is properly annotated to represent the relationships, such as @OneToMany, @ManyToOne, and @JoinColumn.
  4. Repository Layer:
    • Create repository interfaces for each entity:
      • StudentRepository, CourseRepository, and EnrollmentRepository should extend JpaRepository.
    • These repositories will provide CRUD operations and any custom queries you may need later.
  5. Service Layer:
    • Create service classes for each entity, such as StudentService, CourseService, and EnrollmentService.
    • Each service class should contain business logic to handle CRUD operations and manage relationships between entities.
    • Inject the corresponding repository into each service to interact with the database.
  6. Controller Layer:

    • Create REST controllers to expose the following endpoints:

    a. Student Endpoints:

    • Create a new student:
      • Endpoint: POST /api/students
      • Request Body: JSON object with student details.
      • Response: JSON object representing the created student.
    • Retrieve all students:
      • Endpoint: GET /api/students
      • Response: JSON array of student objects.
    • Retrieve a student by ID:
      • Endpoint: GET /api/students/{id}
      • Path Variable: id (the student's ID).
      • Response: JSON object representing the student.
    • Update a student:
      • Endpoint: PUT /api/students/{id}
      • Path Variable: id (the student's ID).
      • Request Body: JSON object with updated student details.
      • Response: Updated JSON object of the student.
    • Delete a student:
      • Endpoint: DELETE /api/students/{id}
      • Path Variable: id (the student's ID).
      • Response: Confirmation message or status code indicating the deletion.

    b. Course Endpoints:

    • Create a new course:
      • Endpoint: POST /api/courses
      • Request Body: JSON object with course details.
      • Response: JSON object representing the created course.
    • Retrieve all courses:
      • Endpoint: GET /api/courses
      • Response: JSON array of course objects.
    • Retrieve a course by ID:
      • Endpoint: GET /api/courses/{id}
      • Path Variable: id (the course's ID).
      • Response: JSON object representing the course.
    • Update a course:
      • Endpoint: PUT /api/courses/{id}
      • Path Variable: id (the course's ID).
      • Request Body: JSON object with updated course details.
      • Response: Updated JSON object of the course.
    • Delete a course:
      • Endpoint: DELETE /api/courses/{id}
      • Path Variable: id (the course's ID).
      • Response: Confirmation message or status code indicating the deletion.

    c. Enrollment Endpoints:

    • Enroll a student in a course:
      • Endpoint: POST /api/enrollments
      • Request Body: JSON object with studentId, courseId, enrollmentDate, and grade.
      • Response: JSON object representing the created enrollment.
    • Retrieve all enrollments:
      • Endpoint: GET /api/enrollments
      • Response: JSON array of enrollment objects.
    • Retrieve enrollments for a specific student:
      • Endpoint: GET /api/students/{studentId}/enrollments
      • Path Variable: studentId.
      • Response: JSON array of enrollment objects for the specified student.
    • Retrieve enrollments for a specific course:
      • Endpoint: GET /api/courses/{courseId}/enrollments
      • Path Variable: courseId.
      • Response: JSON array of enrollment objects for the specified course.
    • Update an enrollment:
      • Endpoint: PUT /api/enrollments/{id}
      • Path Variable: id (the enrollment's ID).
      • Request Body: JSON object with updated enrollment details.
      • Response: Updated JSON object of the enrollment.
    • Delete an enrollment:
      • Endpoint: DELETE /api/enrollments/{id}
      • Path Variable: id (the enrollment's ID).
      • Response: Confirmation message or status code indicating the deletion.
  7. Validation:
    • Add validation to ensure:
      • Required fields are not null.
      • The email field in the Student entity is unique and follows a valid email format.
      • The courseCode field in the Course entity is unique.
      • The grade field in the Enrollment entity follows a specific format (e.g., A, B, C, etc.).
  8. Error Handling:
    • Implement error handling for:
      • Entity not found (e.g., when trying to retrieve or delete a non-existent student, course, or enrollment).
      • Invalid input data (e.g., missing required fields, invalid data format, etc.).
    • Return appropriate HTTP status codes (e.g., 404 for not found, 400 for bad request).
  9. Relationships:
    • Ensure that the relationships between entities (Student, Course, and Enrollment) are correctly handled in both the database and the application.
    • Test that creating, updating, or deleting a student or course also correctly updates or deletes related enrollments.

The task is submitted using Postman collection and code snippets as usual in addition to screenshots from the database

MohammadHodroj commented 2 months ago

Advanced Student Management System

I will only show the relevant code snippets after task 3. For the full project, you can download it here: task4-student-ms.zip

Controllers

Student Controller

package com.dgpad.task4_student_ms.controller;

import com.dgpad.task4_student_ms.DTO.request.CreateStudentDTO;
import com.dgpad.task4_student_ms.DTO.request.UpdateStudentDTO;
import com.dgpad.task4_student_ms.DTO.response.ApiResponse;
import com.dgpad.task4_student_ms.model.Student;
import com.dgpad.task4_student_ms.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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;

    @PostMapping
    public ResponseEntity<ApiResponse<Student>> createStudent(@RequestBody CreateStudentDTO student) {
        try {
            Student savedStudent = studentService.saveStudent(student);
            return ResponseEntity.ok(new ApiResponse<>(true, savedStudent, "Student created successfully", HttpStatus.OK.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to create Student: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @GetMapping
    public ResponseEntity<ApiResponse<List<Student>>> getAllStudents() {
        try {
            List<Student> students = studentService.getAllStudents();
            return ResponseEntity.ok(new ApiResponse<>(true, students, "Students retrieved successfully", HttpStatus.OK.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to retrieve Students: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<Student>> getStudentById(@PathVariable Long id) {
        try {
            Student student = studentService.getStudentById(id);
            return ResponseEntity.ok(new ApiResponse<>(true, student, "Student retrieved successfully", HttpStatus.OK.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to retrieve Student: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @PutMapping("/{id}")
    public ResponseEntity<ApiResponse<Student>> updateStudent(@PathVariable Long id, @RequestBody UpdateStudentDTO student) {
        try {
            Student updatedStudent = studentService.updateStudent(id, student);
            return ResponseEntity.ok(new ApiResponse<>(true, updatedStudent, "Student updated successfully", HttpStatus.OK.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to update Student: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<ApiResponse<Void>> deleteStudent(@PathVariable Long id) {
        try {
            studentService.deleteStudent(id);
            return ResponseEntity.ok(new ApiResponse<>(true, null, "Student deleted successfully", HttpStatus.OK.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to delete Student: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }
}

Course Controller

package com.dgpad.task4_student_ms.controller;

import com.dgpad.task4_student_ms.DTO.request.CreateCourseDTO;
import com.dgpad.task4_student_ms.DTO.request.UpdateCourseDTO;
import com.dgpad.task4_student_ms.DTO.response.ApiResponse;
import com.dgpad.task4_student_ms.model.Course;
import com.dgpad.task4_student_ms.service.CourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/courses")
public class CourseController {

    @Autowired
    private CourseService courseService;

    @PostMapping
    public ResponseEntity<ApiResponse<Course>> createCourse(@RequestBody CreateCourseDTO course)
    {
        try
        {
            Course savedCourse = courseService.saveCourse(course);
            return ResponseEntity.ok(new ApiResponse<>(true, savedCourse, "Course created successfully", HttpStatus.OK.value()));
        }
        catch (Exception e)
        {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to create Course: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @GetMapping
    public ResponseEntity<ApiResponse<List<Course>>> getAllCourses()
    {
        try
        {
            List<Course> courses = courseService.getAllCourses();
            return ResponseEntity.ok(new ApiResponse<>(true, courses, "Courses retrieved successfully", HttpStatus.OK.value()));
        }
        catch (Exception e)
        {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to retrieve Courses: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<Course>> getCourseById(@PathVariable Long id)
    {
        try
        {
            Course course = courseService.getCourseById(id);
            return ResponseEntity.ok(new ApiResponse<>(true, course, "Course retrieved successfully", HttpStatus.OK.value()));
        }
        catch (Exception e)
        {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to retrieve Course: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @PutMapping("/{id}")
    public ResponseEntity<ApiResponse<Course>> updateCourse(@PathVariable Long id, @RequestBody UpdateCourseDTO course)
    {
        try
        {
            Course updatedCourse = courseService.updateCourse(id, course);
            return ResponseEntity.ok(new ApiResponse<>(true, updatedCourse, "Course updated successfully", HttpStatus.OK.value()));
        }
        catch (Exception e)
        {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to update Course: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<ApiResponse<Void>> deleteCourse(@PathVariable Long id)
    {
        try
        {
            courseService.deleteCourse(id);
            return ResponseEntity.ok(new ApiResponse<>(true, null, "Course deleted successfully", HttpStatus.OK.value()));
        }
        catch (Exception e)
        {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to delete Course: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }
}

Enrollment Controller

package com.dgpad.task4_student_ms.controller;

import com.dgpad.task4_student_ms.DTO.request.CreateEnrollmentDTO;
import com.dgpad.task4_student_ms.DTO.request.UpdateEnrollmentDTO;
import com.dgpad.task4_student_ms.DTO.response.ApiResponse;
import com.dgpad.task4_student_ms.DTO.response.EnrollmentResponseDTO;
import com.dgpad.task4_student_ms.service.EnrollmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api")
public class EnrollmentController {

    @Autowired
    private EnrollmentService enrollmentService;

    @PostMapping("/enrollments")
    public ResponseEntity<ApiResponse<EnrollmentResponseDTO>> createEnrollment(@RequestBody CreateEnrollmentDTO enrollmentDTO) {
        try {
            EnrollmentResponseDTO enrollmentDetails = enrollmentService.saveEnrollment(enrollmentDTO);
            return ResponseEntity.ok(new ApiResponse<>(true, enrollmentDetails, "Enrollment created successfully", HttpStatus.CREATED.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to create enrollment: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @GetMapping("/enrollments")
    public ResponseEntity<ApiResponse<List<EnrollmentResponseDTO>>> getAllEnrollments() {
        try {
            List<EnrollmentResponseDTO> enrollments = enrollmentService.getAllEnrollments();
            return ResponseEntity.ok(new ApiResponse<>(true, enrollments, "Enrollments retrieved successfully", HttpStatus.OK.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to retrieve enrollments: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @GetMapping("/students/{studentId}/enrollments")
    public ResponseEntity<ApiResponse<List<EnrollmentResponseDTO>>> getEnrollmentsForStudent(@PathVariable Long studentId) {
        try {
            List<EnrollmentResponseDTO> enrollments = enrollmentService.getEnrollmentsForStudent(studentId);
            return ResponseEntity.ok(new ApiResponse<>(true, enrollments, "Enrollments for student retrieved successfully", HttpStatus.OK.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to retrieve enrollments for student: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @GetMapping("/courses/{courseId}/enrollments")
    public ResponseEntity<ApiResponse<List<EnrollmentResponseDTO>>> getEnrollmentsForCourse(@PathVariable Long courseId) {
        try {
            List<EnrollmentResponseDTO> enrollments = enrollmentService.getEnrollmentsForCourse(courseId);
            return ResponseEntity.ok(new ApiResponse<>(true, enrollments, "Enrollments for course retrieved successfully", HttpStatus.OK.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to retrieve enrollments for course: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @PutMapping("/enrollments/{id}")
    public ResponseEntity<ApiResponse<EnrollmentResponseDTO>> updateEnrollment(@PathVariable Long id, @RequestBody UpdateEnrollmentDTO enrollmentDTO) {
        try {
            EnrollmentResponseDTO updatedEnrollment = enrollmentService.updateEnrollment(id, enrollmentDTO);
            return updatedEnrollment != null
                    ? ResponseEntity.ok(new ApiResponse<>(true, updatedEnrollment, "Enrollment updated successfully", HttpStatus.OK.value()))
                    : ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .body(new ApiResponse<>(false, null, "Enrollment not found", HttpStatus.NOT_FOUND.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to update enrollment: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }

    @DeleteMapping("/enrollments/{id}")
    public ResponseEntity<ApiResponse<Void>> deleteEnrollment(@PathVariable Long id) {
        try {
            enrollmentService.deleteEnrollment(id);
            return ResponseEntity.ok(new ApiResponse<>(true, null, "Enrollment deleted successfully", HttpStatus.NO_CONTENT.value()));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse<>(false, null, "Failed to delete enrollment: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()));
        }
    }
}

Repositories

Student Repository

package com.dgpad.task4_student_ms.repository;

import com.dgpad.task4_student_ms.model.Student;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudentRepository extends JpaRepository<Student, Long> {
}

Course Repository

package com.dgpad.task4_student_ms.repository;

import com.dgpad.task4_student_ms.model.Course;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CourseRepository extends JpaRepository<Course, Long> {
}

Enrollment Repository

package com.dgpad.task4_student_ms.repository;

import com.dgpad.task4_student_ms.model.Enrollment;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface EnrollmentRepository extends JpaRepository<Enrollment, Long> {
    List<Enrollment> findByStudentId(Long studentId);
    List<Enrollment> findByCourseId(Long courseId);
}

Services

Student Service

package com.dgpad.task4_student_ms.service;

import com.dgpad.task4_student_ms.DTO.request.CreateStudentDTO;
import com.dgpad.task4_student_ms.DTO.request.UpdateStudentDTO;
import com.dgpad.task4_student_ms.model.Enrollment;
import com.dgpad.task4_student_ms.model.Student;
import com.dgpad.task4_student_ms.repository.EnrollmentRepository;
import com.dgpad.task4_student_ms.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class StudentService {

    @Autowired
    private StudentRepository studentRepository;

    @Autowired
    private EnrollmentRepository enrollmentRepository;

    public Student saveStudent(CreateStudentDTO studentDTO)
    {
        Student student = new Student(studentDTO.getFirstName(), studentDTO.getLastName(), studentDTO.getEmail(), studentDTO.getAge());
        return studentRepository.save(student);
    }

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

    public Student getStudentById(Long id)
    {
        return studentRepository.findById(id).orElse(null);
    }

    public Student updateStudent(Long id, UpdateStudentDTO updatedStudentDTO)
    {
        Optional<Student> existingStudentOptional = studentRepository.findById(id);

        if (existingStudentOptional.isPresent())
        {
            Student existingStudent = existingStudentOptional.get();
            existingStudent.setFirstName(updatedStudentDTO.getFirstName());
            existingStudent.setLastName(updatedStudentDTO.getLastName());
            existingStudent.setEmail(updatedStudentDTO.getEmail());
            existingStudent.setAge(updatedStudentDTO.getAge());
            return studentRepository.save(existingStudent);
        }
        else
        {
            return null;
        }
    }

    public void deleteStudent(Long id)
    {
        studentRepository.deleteById(id);
    }

    public int getTotalCredits(Long studentId)
    {
        List<Enrollment> enrollments = enrollmentRepository.findByStudentId(studentId);
        return enrollments.stream()
                .mapToInt(enrollment -> enrollment.getCourse().getCredits())
                .sum();
    }
}

Course Service

package com.dgpad.task4_student_ms.service;

import com.dgpad.task4_student_ms.DTO.request.CreateCourseDTO;
import com.dgpad.task4_student_ms.DTO.request.UpdateCourseDTO;
import com.dgpad.task4_student_ms.model.Course;
import com.dgpad.task4_student_ms.repository.CourseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class CourseService {

    @Autowired
    private CourseRepository courseRepository;

    public Course saveCourse(CreateCourseDTO courseDTO)
    {
        Course course = new Course(courseDTO.getName(), courseDTO.getCode(), courseDTO.getInstructor(), courseDTO.getCredits());
        return courseRepository.save(course);
    }

    public List<Course> getAllCourses()
    {
        return courseRepository.findAll();
    }

    public Course getCourseById(Long id)
    {
        return courseRepository.findById(id).orElse(null);
    }

    public Course updateCourse(Long id, UpdateCourseDTO updatedCourseDTO)
    {
        Optional<Course> existingCourseOptional = courseRepository.findById(id);

        if (existingCourseOptional.isPresent())
        {
            Course existingCourse = existingCourseOptional.get();
            existingCourse.setCourseName(updatedCourseDTO.getName());
            existingCourse.setCourseCode(updatedCourseDTO.getCode());
            existingCourse.setInstructor(updatedCourseDTO.getInstructor());
            existingCourse.setCredits(updatedCourseDTO.getCredits());
            return courseRepository.save(existingCourse);
        }
        else
        {
            return null;
        }
    }

    public void deleteCourse(Long id)
    {
        courseRepository.deleteById(id);
    }
}

Enrollment Service

package com.dgpad.task4_student_ms.service;

import com.dgpad.task4_student_ms.DTO.request.CreateEnrollmentDTO;
import com.dgpad.task4_student_ms.DTO.request.UpdateEnrollmentDTO;
import com.dgpad.task4_student_ms.DTO.response.EnrollmentResponseDTO;
import com.dgpad.task4_student_ms.model.Course;
import com.dgpad.task4_student_ms.model.Enrollment;
import com.dgpad.task4_student_ms.model.Student;
import com.dgpad.task4_student_ms.repository.CourseRepository;
import com.dgpad.task4_student_ms.repository.EnrollmentRepository;
import com.dgpad.task4_student_ms.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class EnrollmentService {

    @Autowired
    private EnrollmentRepository enrollmentRepository;

    @Autowired
    private StudentRepository studentRepository;

    @Autowired
    private CourseRepository courseRepository;

    @Autowired
    private StudentService studentService;

    private static final int MAX_CREDITS = 17;

    public EnrollmentResponseDTO saveEnrollment(CreateEnrollmentDTO enrollmentDTO)
    {
        Optional<Student> studentOptional = studentRepository.findById(enrollmentDTO.getStudentId());
        Optional<Course> courseOptional = courseRepository.findById(enrollmentDTO.getCourseId());

        if (studentOptional.isPresent() && courseOptional.isPresent()) {
            Student student = studentOptional.get();
            Course course = courseOptional.get();

            int totalCredits = studentService.getTotalCredits(student.getId());

            if (totalCredits + course.getCredits() > MAX_CREDITS) {
                throw new IllegalArgumentException("Student cannot exceed the maximum allowed credits.");
            }

            Enrollment enrollment = new Enrollment();
            enrollment.setStudent(student);
            enrollment.setCourse(course);
            enrollment.setEnrollmentDate(enrollmentDTO.getEnrollmentDate());
            enrollment.setGrade(enrollmentDTO.getGrade());

            Enrollment savedEnrollment = enrollmentRepository.save(enrollment);

            return new EnrollmentResponseDTO(
                    savedEnrollment.getId(),
                    savedEnrollment.getEnrollmentDate(),
                    savedEnrollment.getGrade(),
                    savedEnrollment.getStudent(),
                    savedEnrollment.getCourse()
            );
        }
        return null;
    }

    public List<EnrollmentResponseDTO> getAllEnrollments() {
        List<Enrollment> enrollments = enrollmentRepository.findAll();
        return enrollments.stream()
                .map(enrollment -> new EnrollmentResponseDTO(
                        enrollment.getId(),
                        enrollment.getEnrollmentDate(),
                        enrollment.getGrade(),
                        enrollment.getStudent(),
                        enrollment.getCourse()
                ))
                .collect(Collectors.toList());
    }

    public List<EnrollmentResponseDTO> getEnrollmentsForStudent(Long studentId) {
        List<Enrollment> enrollments = enrollmentRepository.findByStudentId(studentId);
        return enrollments.stream()
                .map(enrollment -> new EnrollmentResponseDTO(
                        enrollment.getId(),
                        enrollment.getEnrollmentDate(),
                        enrollment.getGrade(),
                        enrollment.getStudent(),
                        enrollment.getCourse()
                ))
                .collect(Collectors.toList());
    }

    public List<EnrollmentResponseDTO> getEnrollmentsForCourse(Long courseId) {
        List<Enrollment> enrollments = enrollmentRepository.findByCourseId(courseId);
        return enrollments.stream()
                .map(enrollment -> new EnrollmentResponseDTO(
                        enrollment.getId(),
                        enrollment.getEnrollmentDate(),
                        enrollment.getGrade(),
                        enrollment.getStudent(),
                        enrollment.getCourse()
                ))
                .collect(Collectors.toList());
    }

    public EnrollmentResponseDTO updateEnrollment(Long id, UpdateEnrollmentDTO enrollmentDTO) {
        Optional<Enrollment> existingEnrollmentOptional = enrollmentRepository.findById(id);

        if (existingEnrollmentOptional.isPresent()) {
            Enrollment existingEnrollment = existingEnrollmentOptional.get();
            existingEnrollment.setGrade(enrollmentDTO.getGrade());
            existingEnrollment.setEnrollmentDate(enrollmentDTO.getEnrollmentDate());
            Enrollment updatedEnrollment = enrollmentRepository.save(existingEnrollment);

            return new EnrollmentResponseDTO(
                    updatedEnrollment.getId(),
                    updatedEnrollment.getEnrollmentDate(),
                    updatedEnrollment.getGrade(),
                    updatedEnrollment.getStudent(),
                    updatedEnrollment.getCourse()
            );
        }
        return null;
    }

    public void deleteEnrollment(Long id) {
        enrollmentRepository.deleteById(id);
    }
}

Postman APIs

Student

createStudent

createStudent

getAllStudents

getAllStudents

getStudentById

getStudentById

updateStudent

updateStudent

deleteStudent

deleteStudent

Course

createCourse

createCourse

getAllCourses

getAllCourses

getCourseById

getCourseById

updateCourse

updateCourse

deleteCourse

deleteCourse

Enrollment

createEnrollment

createEnrollment

getAllEnrollments

getAllEnrollments

getEnrollmentsForStudent

getAllEnrollmentsForStudent

getEnrollmentsForCourse

getAllEnrollmentsForCourse

updateEnrollment

updateEnrollment

deleteEnrollment

deleteEnrollment

Database

Students

students_table

Courses

courses_table

Enrollments

enrollments_table