Complete CRUD Example in Spring Boot with DTO Validation, and Common API Response using MySQL
Here’s a complete CRUD example for the Student and Course modules using Spring Boot, MySQL, a one-to-many relationship, DTO validation, a common API Response, and Postman testing.

spring-boot-student-course-crud/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── studentcourse/
│ │ │ ├── controller/
│ │ │ │ └── StudentController.java
│ │ │ ├── dto/
│ │ │ │ ├── StudentDTO.java
│ │ │ │ ├── CourseDTO.java
│ │ │ ├── entity/
│ │ │ │ ├── Student.java
│ │ │ │ ├── Course.java
│ │ │ ├── exception/
│ │ │ │ ├── ResourceNotFoundException.java
│ │ │ ├── repository/
│ │ │ │ ├── StudentRepository.java
│ │ │ │ ├── CourseRepository.java
│ │ │ ├── service/
│ │ │ │ └── StudentService.java
│ │ │ ├── response/
│ │ │ │ └── ApiResponse.java
│ │ │ └── SpringBootStudentCourseCrudApplication.java
│ │ ├── resources/
│ │ │ ├── application.properties
│ │ │ └── data.sql
│ ├── test/
│ │ └── java/
│ │ └── com/
│ │ └── example/
│ │ └── studentcourse/
│ │ └── SpringBootStudentCourseCrudApplicationTests.java
├── .gitignore
├── mvnw
├── mvnw.cmd
├── pom.xml
└── README.mdStep 1: Project Setup
- Dependencies: Spring Web, Spring Data JPA, MySQL Driver, Lombok, Spring Boot Validation, and Spring DevTools.
pom.xml:
<dependencies>
<!-- Spring Boot Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>Step 2: MySQL Database Configuration
In application.properties or application.yml:
spring.datasource.url=jdbc:mysql://localhost:3306/school_db
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=trueStep 3: Entity Classes
Student.java
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Name is mandatory")
@Size(min = 3, max = 50, message = "Name should be between 3 to 50 characters")
private String name;
@Email(message = "Email should be valid")
private String email;
@OneToMany(mappedBy = "student", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Course> courses = new ArrayList<>();
}Course.java
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Course name is mandatory")
private String courseName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "student_id")
private Student student;
}Step 4: DTO Classes
StudentDTO.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {
private Long id;
@NotBlank(message = "Name is mandatory")
private String name;
@Email(message = "Email should be valid")
private String email;
private List<CourseDTO> courses;
}CourseDTO.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CourseDTO {
private Long id;
@NotBlank(message = "Course name is mandatory")
private String courseName;
}Step 5: Common API Response
ApiResponse.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
}Step 6: Repositories
StudentRepository.java
@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
}CourseRepository.java
@Repository
public interface CourseRepository extends JpaRepository<Course, Long> {
}Step 7: Service Layer
StudentService.java
@Service
public class StudentService {
@Autowired
private StudentRepository studentRepository;
@Autowired
private CourseRepository courseRepository;
// Create Student
public ApiResponse<StudentDTO> createStudent(StudentDTO studentDTO) {
Student student = new Student();
student.setName(studentDTO.getName());
student.setEmail(studentDTO.getEmail());
List<Course> courses = studentDTO.getCourses().stream().map(courseDTO -> {
Course course = new Course();
course.setCourseName(courseDTO.getCourseName());
course.setStudent(student);
return course;
}).collect(Collectors.toList());
student.setCourses(courses);
studentRepository.save(student);
return new ApiResponse<>(true, "Student created successfully", studentDTO);
}
// Get All Students
public ApiResponse<List<StudentDTO>> getAllStudents() {
List<StudentDTO> students = studentRepository.findAll().stream().map(student -> {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(student.getId());
studentDTO.setName(student.getName());
studentDTO.setEmail(student.getEmail());
studentDTO.setCourses(student.getCourses().stream().map(course -> {
CourseDTO courseDTO = new CourseDTO();
courseDTO.setId(course.getId());
courseDTO.setCourseName(course.getCourseName());
return courseDTO;
}).collect(Collectors.toList()));
return studentDTO;
}).collect(Collectors.toList());
return new ApiResponse<>(true, "List of all students", students);
}
// Get Student by ID
public ApiResponse<StudentDTO> getStudentById(Long id) {
Student student = studentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Student not found with id: " + id));
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(student.getId());
studentDTO.setName(student.getName());
studentDTO.setEmail(student.getEmail());
studentDTO.setCourses(student.getCourses().stream().map(course -> {
CourseDTO courseDTO = new CourseDTO();
courseDTO.setId(course.getId());
courseDTO.setCourseName(course.getCourseName());
return courseDTO;
}).collect(Collectors.toList()));
return new ApiResponse<>(true, "Student found", studentDTO);
}
// Update Student
public ApiResponse<StudentDTO> updateStudent(Long id, StudentDTO studentDTO) {
Student student = studentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Student not found with id: " + id));
student.setName(studentDTO.getName());
student.setEmail(studentDTO.getEmail());
student.getCourses().clear();
List<Course> courses = studentDTO.getCourses().stream().map(courseDTO -> {
Course course = new Course();
course.setCourseName(courseDTO.getCourseName());
course.setStudent(student);
return course;
}).collect(Collectors.toList());
student.setCourses(courses);
studentRepository.save(student);
return new ApiResponse<>(true, "Student updated successfully", studentDTO);
}
// Delete Student
public ApiResponse<Void> deleteStudent(Long id) {
Student student = studentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Student not found with id: " + id));
studentRepository.delete(student);
return new ApiResponse<>(true, "Student deleted successfully", null);
}
}ResourceNotFoundException.java
package com.example.studentcourse.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) {
super(message);
}
}Step 8: Controller Layer
StudentController.java
@RestController
@RequestMapping("/api/students")
public class StudentController {
@Autowired
private StudentService studentService;
// Create Student
@PostMapping
public ResponseEntity<ApiResponse<StudentDTO>> createStudent(@Valid @RequestBody StudentDTO studentDTO) {
ApiResponse<StudentDTO> response = studentService.createStudent(studentDTO);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
// Get All Students
@GetMapping
public ResponseEntity<ApiResponse<List<StudentDTO>>> getAllStudents() {
ApiResponse<List<StudentDTO>> response = studentService.getAllStudents();
return new ResponseEntity<>(response, HttpStatus.OK);
}
// Get Student by ID
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<StudentDTO>> getStudentById(@PathVariable Long id) {
ApiResponse<StudentDTO> response = studentService.getStudentById(id);
return new ResponseEntity<>(response, HttpStatus.OK);
}
// Update Student
@PutMapping("/{id}")
public ResponseEntity<ApiResponse<StudentDTO>> updateStudent(@PathVariable Long id, @Valid @RequestBody StudentDTO studentDTO) {
ApiResponse<StudentDTO> response = studentService.updateStudent(id, studentDTO);
return new ResponseEntity<>(response, HttpStatus.OK);
}
// Delete Student
@DeleteMapping("/{id}")
public ResponseEntity<ApiResponse<Void>> deleteStudent(@PathVariable Long id) {
ApiResponse<Void> response = studentService.deleteStudent(id);
return new ResponseEntity<>(response, HttpStatus.OK);
}
}Step 9: Testing with Postman
- POST
/api/students
- Body:
{
"name": "John Doe",
"email": "[email protected]",
"courses": [
{"courseName": "Mathematics"},
{"courseName": "Physics"}
]
}Response:
{
"success": true,
"message": "Student created successfully",
"data": {
"name": "John Doe",
"email": "[email protected]",
"courses": [
{"courseName": "Mathematics"},
{"courseName": "Physics"}
]
}
}GET /api/students
- Response:
[
{
"id": 1,
"name": "John Doe",
"email": "[email protected]",
"courses": [
{"id": 1, "courseName": "Mathematics"},
{"id": 2, "courseName": "Physics"}
]
}
]Update Student
Endpoint:
PUT /api/students/{id}
- Method:
PUT - URL:
http://localhost:8080/api/students/1(Assuming1is the student ID you want to update) - Request Body (JSON):
{
"name": "John Updated",
"email": "[email protected]",
"courses": [
{
"courseName": "Updated Mathematics"
},
{
"courseName": "Updated Physics"
}
]
}Response:
{
"success": true,
"message": "Student updated successfully",
"data": {
"id": 1,
"name": "John Updated",
"email": "[email protected]",
"courses": [
{
"id": 1,
"courseName": "Updated Mathematics"
},
{
"id": 2,
"courseName": "Updated Physics"
}
]
}
}2. Delete Student
Endpoint:
DELETE /api/students/{id}
- Method:
DELETE - URL:
http://localhost:8080/api/students/1(Assuming1is the student ID you want to delete) - Response:
{
"success": true,
"message": "Student deleted successfully",
"data": null
}Step 10: Conclusion
This example demonstrates how to create a complete CRUD application with Student and Course modules, using a one-to-many relationship, DTO validation, and a common API response structure. You can expand this with update and delete functionality similarly.
👏 If you found my articles useful, please consider giving it claps and sharing it with your friends and colleagues.
To read other topics
- Spring Boot Validation Annotations
- One To One mapping in Spring Boot JPA
- One To Many mapping in Spring Boot JPA
- Api Response and Global Exception Handling in Spring Boot
- @AssociationOverride, @AttributeOverrides, @Embeddable, @Embedded Annotation.
- Mastering Transaction Propagation and Isolation in Spring Boot
- Pessimistic Locking in JPA with Spring Boot
- Optimistic Locking in JPA with Spring Boot
- Optional in Java 8





