Performing CRUD Operations Using JpaRepository
Course: Advanced Java Training
Topic: Performing CRUD Operations Using JpaRepository
Course Link: Advanced Java Training Course
Demo Link: Register for a Course Demo
Website: JustAcademy
1. Introduction to CRUD Operations Using JpaRepository
CRUD stands for Create, Read, Update, and Delete. These four operations form the foundation of most database-driven applications. A student management system creates student records, retrieves student information, updates existing details, and deletes records when necessary.
In Spring Boot applications, Spring Data JPA simplifies database operations through repository interfaces. By extending JpaRepository, developers can use built-in methods for common persistence tasks without manually implementing every database operation.
JpaRepository works with JPA entities and a persistence provider such as Hibernate. It provides common CRUD methods along with pagination, sorting, and other JPA-related functionality.
Learning Objectives
- Understand CRUD operations and their importance.
- Learn how
JpaRepository simplifies database access.
- Create entity classes and repository interfaces.
- Insert new records into a database.
- Retrieve all records and individual records by ID.
- Update existing records safely.
- Delete records using repository methods.
- Build REST APIs for CRUD operations.
- Handle validation, missing records, and database errors.
- Apply transaction-management and persistence best practices.
2. What Is JpaRepository?
JpaRepository is an interface provided by Spring Data JPA. It combines common repository functionality with JPA-specific capabilities so developers can perform database operations with minimal boilerplate code.
Instead of manually writing SQL for every basic operation, developers can define a repository interface and let Spring Data JPA generate its implementation at runtime.
Basic Syntax
public interface StudentRepository
extends JpaRepository<Student, Long> {
}
In this example:
StudentRepository is the repository interface.
Student is the JPA entity type.
Long is the type of the entity's primary key.
- Spring Data JPA provides the implementation for inherited repository methods.
3. Understanding the CRUD Architecture
Client / Postman
|
v
REST Controller
|
v
Service Layer
|
v
JpaRepository
|
v
JPA / Hibernate
|
v
DataSource and JDBC Driver
|
v
Relational Database
Component responsibilities:
- Controller: Accepts HTTP requests and returns appropriate responses.
- Service: Implements business logic and coordinates operations.
- Repository: Provides methods for accessing persistent data.
- JPA/Hibernate: Maps entity objects to database records and manages persistence.
- Database: Stores the application's data.
Keeping these responsibilities separate improves maintainability, testing, and code reuse.
4. Required Dependencies
For a typical Maven-based Spring Boot REST application using JPA and MySQL, include the following dependencies. The Spring Boot parent or dependency-management configuration should manage compatible versions.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
Dependency purposes:
spring-boot-starter-data-jpa provides Spring Data JPA and its JPA integration.
spring-boot-starter-web supports REST controllers and HTTP endpoints.
mysql-connector-j provides the JDBC driver for MySQL.
spring-boot-starter-validation enables Bean Validation annotations such as @NotBlank and @Email.
5. Configuring the Database Connection
Create a database in MySQL before starting the application.
CREATE DATABASE training_db;
Configure the connection in src/main/resources/application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/training_db
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Set DB_USERNAME and DB_PASSWORD in the application's runtime environment. Avoid committing real database credentials to source control.
The ddl-auto=update option may be convenient during development. For production applications, prefer versioned schema migrations and an appropriate setting such as validate or none.
6. Creating the JPA Entity
A JPA entity represents persistent data. In this tutorial, a Student entity stores the student's ID, name, email, and course.
Student.java
package com.example.demo.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "students")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Column(nullable = false, unique = true, length = 150)
private String email;
@Column(nullable = false, length = 100)
private String course;
protected Student() {
}
public Student(String name, String email, String course) {
this.name = name;
this.email = email;
this.course = course;
}
public Long getId() {
return id;
}
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 getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
}
Important annotations:
@Entity marks the class as a JPA entity.
@Table(name = "students") maps the entity to the students table.
@Id identifies the primary key.
@GeneratedValue configures identifier generation.
@Column defines column-related mapping options.
For Spring Boot 3 and later, use jakarta.persistence imports. Older Spring Boot 2 projects commonly use javax.persistence.
7. Creating the Repository Interface
Create a repository interface that extends JpaRepository. No manual implementation is needed for the standard CRUD methods.
StudentRepository.java
package com.example.demo.repository;
import com.example.demo.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface StudentRepository
extends JpaRepository<Student, Long> {
Optional<Student> findByEmail(String email);
List<Student> findByCourse(String course);
}
The inherited methods handle common database operations. The additional methods demonstrate Spring Data JPA's derived-query feature, which generates queries from method names.
8. CRUD Operation 1: Create
The Create operation inserts a new record into the database. In Spring Data JPA, the common method for creating a record is save().
8.1 Using save()
Student student = new Student(
"Aarav Sharma",
"[email protected]",
"Advanced Java"
);
Student savedStudent = studentRepository.save(student);
When the entity is new, Spring Data JPA and the JPA provider arrange for it to be persisted. The database-generated ID can be available on the returned entity after persistence operations have occurred.
8.2 Create Operation Through a Service
package com.example.demo.service;
import com.example.demo.entity.Student;
import com.example.demo.repository.StudentRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StudentService {
private final StudentRepository repository;
public StudentService(StudentRepository repository) {
this.repository = repository;
}
@Transactional
public Student createStudent(Student student) {
return repository.save(student);
}
}
Key points:
- Create an entity object using valid input data.
- Pass the entity to
save().
- Return the saved entity or map it to a response DTO.
- Handle validation errors and unique-constraint violations.
9. CRUD Operation 2: Read
The Read operation retrieves existing data. JpaRepository provides methods for finding records by ID, retrieving records, checking existence, and counting records.
9.1 Retrieve All Students
List<Student> students = studentRepository.findAll();
This method retrieves all matching entity records. For large tables, use pagination instead of loading the entire dataset into memory.
9.2 Retrieve a Student by ID
Optional<Student> student =
studentRepository.findById(1L);
findById() returns an Optional. This allows the application to handle the possibility that a record does not exist.
Student student = studentRepository.findById(1L)
.orElseThrow(() ->
new RuntimeException("Student not found")
);
In a production application, use a dedicated exception and map it to an appropriate HTTP response rather than exposing a generic runtime exception.
9.3 Check Whether a Record Exists
boolean exists = studentRepository.existsById(1L);
9.4 Count Records
long totalStudents = studentRepository.count();
9.5 Search by Email
Optional<Student> student =
studentRepository.findByEmail("[email protected]");
This custom repository method is derived from the method name. Spring Data JPA generates the query implementation based on the entity's mapped email field.
10. CRUD Operation 3: Update
The Update operation modifies an existing record. With JPA, a common approach is to retrieve the entity, modify its fields, and let the persistence context synchronize the changes with the database.
10.1 Updating an Existing Student
@Transactional
public Student updateStudent(
Long id,
String name,
String email,
String course) {
Student student = repository.findById(id)
.orElseThrow(() ->
new RuntimeException("Student not found")
);
student.setName(name);
student.setEmail(email);
student.setCourse(course);
return student;
}
When the entity is managed within the transaction, JPA can detect the changes and synchronize them with the database at flush or commit time. Calling save() again is often unnecessary for an already managed entity, although it may be used for consistency in some application patterns.
10.2 Updating Using save()
Student student = repository.findById(1L)
.orElseThrow(() ->
new RuntimeException("Student not found")
);
student.setCourse("Spring Boot");
Student updatedStudent = repository.save(student);
Always retrieve and update the existing record when the intended operation is to change only selected fields. Blindly creating a new entity with an existing ID and saving it can lead to unintended behavior or lost field values.
11. CRUD Operation 4: Delete
The Delete operation removes a record from the database. Spring Data JPA provides methods such as delete() and deleteById().
11.1 Delete by ID
repository.deleteById(1L);
This requests deletion of the entity associated with the specified ID. Exact behavior when the ID does not exist can depend on the Spring Data version and execution path, so applications should define their desired not-found behavior explicitly.
11.2 Check Before Deleting
@Transactional
public void deleteStudent(Long id) {
Student student = repository.findById(id)
.orElseThrow(() ->
new RuntimeException("Student not found")
);
repository.delete(student);
}
This approach allows the service to detect a missing record before attempting deletion. A custom not-found exception can be translated into HTTP 404 by the web layer.
12. JpaRepository CRUD Methods Summary
| Method |
Operation |
Purpose |
save(entity) |
Create / Update |
Saves an entity according to its persistence state. |
findAll() |
Read |
Retrieves all entities. |
findById(id) |
Read |
Retrieves an entity by primary key. |
existsById(id) |
Read |
Checks whether an entity exists. |
count() |
Read |
Counts the entities. |
delete(entity) |
Delete |
Deletes the specified entity. |
deleteById(id) |
Delete |
Deletes an entity by primary key. |
findAll(pageable) |
Read |
Retrieves a page of entities. |
13. Creating a Complete Service Layer
The service layer groups database operations and business rules into a reusable component.
StudentService.java
package com.example.demo.service;
import com.example.demo.entity.Student;
import com.example.demo.repository.StudentRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class StudentService {
private final StudentRepository repository;
public StudentService(StudentRepository repository) {
this.repository = repository;
}
@Transactional
public Student createStudent(Student student) {
return repository.save(student);
}
@Transactional(readOnly = true)
public List<Student> getAllStudents() {
return repository.findAll();
}
@Transactional(readOnly = true)
public Student getStudentById(Long id) {
return repository.findById(id)
.orElseThrow(() ->
new StudentNotFoundException(
"Student not found with ID: " + id
)
);
}
@Transactional
public Student updateStudent(
Long id,
Student request) {
Student existing = getStudentById(id);
existing.setName(request.getName());
existing.setEmail(request.getEmail());
existing.setCourse(request.getCourse());
return existing;
}
@Transactional
public void deleteStudent(Long id) {
Student existing = getStudentById(id);
repository.delete(existing);
}
}
Create a dedicated exception class for missing records:
package com.example.demo.service;
public class StudentNotFoundException
extends RuntimeException {
public StudentNotFoundException(String message) {
super(message);
}
}
The update method changes the existing managed entity inside a transaction. JPA can persist the changed fields when the transaction completes successfully.
14. Creating REST Endpoints for CRUD Operations
A REST controller exposes CRUD functionality through HTTP methods. A typical mapping is POST for creation, GET for retrieval, PUT for full updates, and DELETE for deletion.
StudentController.java
package com.example.demo.controller;
import com.example.demo.entity.Student;
import com.example.demo.service.StudentService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/students")
public class StudentController {
private final StudentService service;
public StudentController(StudentService service) {
this.service = service;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Student createStudent(
@Valid @RequestBody Student student) {
return service.createStudent(student);
}
@GetMapping
public List<Student> getAllStudents() {
return service.getAllStudents();
}
@GetMapping("/{id}")
public Student getStudentById(
@PathVariable Long id) {
return service.getStudentById(id);
}
@PutMapping("/{id}")
public Student updateStudent(
@PathVariable Long id,
@Valid @RequestBody Student student) {
return service.updateStudent(id, student);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteStudent(@PathVariable Long id) {
service.deleteStudent(id);
}
}
This controller illustrates the basic API structure. To use @Valid effectively, add validation annotations to the request model or, preferably, to a dedicated request DTO. A global exception handler should translate missing records into HTTP 404 and validation errors into HTTP 400. For production APIs, avoid exposing persistence entities directly when a DTO-based design is more appropriate.
15. Handling Exceptions Gracefully
CRUD applications must handle situations such as a student not existing, invalid request data, and duplicate email addresses. Instead of returning generic server errors, use meaningful exceptions and appropriate HTTP status codes.
15.1 Global Exception Handler
package com.example.demo.controller;
import com.example.demo.service.StudentNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(StudentNotFoundException.class)
public ResponseEntity<Map<String, String>> handleNotFound(
StudentNotFoundException exception) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(Map.of("error", exception.getMessage()));
}
}
The @RestControllerAdvice annotation allows exception handling to be shared across REST controllers. When a StudentNotFoundException occurs, the handler returns HTTP 404 with an error message.
15.2 Add Validation to the Entity or Request DTO
For applications that use Bean Validation, add constraints to the request model. A dedicated DTO is generally preferable to exposing an entity directly through a public API.
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class StudentRequest {
@NotBlank(message = "Name is required")
@Size(max = 100)
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Enter a valid email address")
@Size(max = 150)
private String email;
@NotBlank(message = "Course is required")
@Size(max = 100)
private String course;
public StudentRequest() {
}
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 getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
}
Validate incoming request DTOs with @Valid in the controller, then map validated values to the entity. Database constraints should still enforce important rules, such as email uniqueness, because application-level validation alone cannot prevent concurrent duplicate inserts.
16. Testing CRUD APIs with Postman or curl
Once the Spring Boot application starts successfully, test each CRUD endpoint using Postman or a similar HTTP client.
| Operation |
HTTP Method |
Endpoint |
Purpose |
| Create |
POST |
/api/students |
Add a new student. |
| Read all |
GET |
/api/students |
Retrieve students. |
| Read one |
GET |
/api/students/1 |
Retrieve a student by ID. |
| Update |
PUT |
/api/students/1 |
Update a student's details. |
| Delete |
DELETE |
/api/students/1 |
Delete a student. |
16.1 Create a Student
Send a POST request to http://localhost:8080/api/students with the following JSON body:
{
"name": "Aarav Sharma",
"email": "[email protected]",
"course": "Advanced Java"
}
Example curl request:
curl -X POST http://localhost:8080/api/students \
-H "Content-Type: application/json" \
-d '{"name":"Aarav Sharma","email":"[email protected]","course":"Advanced Java"}'
The example controller returns HTTP 201 when the student is created successfully.
16.2 Retrieve All Students
curl http://localhost:8080/api/students
The response contains the student records available in the database. The exact response depends on the records and response model used by the application.
16.3 Retrieve One Student
curl http://localhost:8080/api/students/1
If the student exists, the endpoint returns the corresponding record. If it does not exist, the service exception and global exception handler can return HTTP 404.
16.4 Update a Student
curl -X PUT http://localhost:8080/api/students/1 \
-H "Content-Type: application/json" \
-d '{"name":"Aarav Sharma","email":"[email protected]","course":"Spring Boot"}'
This example uses PUT to replace the student's editable details. If an API supports partial updates, PATCH can be implemented with explicit rules for which fields may be changed.
16.5 Delete a Student
curl -X DELETE http://localhost:8080/api/students/1
The example controller returns HTTP 204 after a successful deletion. Define consistent behavior for deleting a record that does not exist.
17. Understanding HTTP Status Codes for CRUD
| Status Code |
Meaning |
Typical CRUD Use |
| 200 OK |
Request completed successfully. |
Reading or updating a resource. |
| 201 Created |
A resource was created successfully. |
Successful POST request. |
| 204 No Content |
Request succeeded without a response body. |
Successful DELETE request. |
| 400 Bad Request |
Request data is invalid. |
Validation failures. |
| 404 Not Found |
The requested resource does not exist. |
Retrieving or updating a missing student. |
| 409 Conflict |
The request conflicts with the current resource state. |
Duplicate email or another uniqueness conflict, when mapped accordingly. |
| 500 Internal Server Error |
An unexpected server error occurred. |
Unexpected application or infrastructure failures. |
18. Using Pagination and Sorting
Calling findAll() may be inefficient when a database contains thousands or millions of records. Pagination retrieves a limited number of records per request, while sorting orders records according to specified fields.
18.1 Pagination in a Repository
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
public interface StudentRepository
extends JpaRepository<Student, Long> {
Page<Student> findByCourse(
String course,
Pageable pageable
);
}
18.2 Calling Pagination from a Service
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StudentSearchService {
private final StudentRepository repository;
public StudentSearchService(StudentRepository repository) {
this.repository = repository;
}
@Transactional(readOnly = true)
public Page<Student> getStudents(int page, int size) {
if (page < 0 || size < 1 || size > 100) {
throw new IllegalArgumentException(
"Invalid pagination parameters"
);
}
return repository.findAll(
PageRequest.of(page, size)
);
}
}
Page indexes start at zero. Limiting the maximum page size helps prevent clients from requesting unnecessarily large result sets.
18.3 Sorting Records
import org.springframework.data.domain.Sort;
List<Student> students = repository.findAll(
Sort.by(Sort.Direction.ASC, "name")
);
Only allow approved entity fields for client-controlled sorting. Validate requested sort properties before passing them to the repository.
19. Difference Between save() and saveAndFlush()
Both methods save an entity, but they differ in when pending persistence changes are synchronized with the database.
| Method |
Behavior |
Typical Use |
save(entity) |
Saves an entity; SQL execution may occur during flushing or transaction completion. |
Normal create and update operations. |
saveAndFlush(entity) |
Saves the entity and flushes pending persistence changes to the database immediately. |
When an immediate flush is specifically needed. |
saveAll(entities) |
Saves multiple entities. |
Saving a collection of records. |
saveAllAndFlush(entities) |
Saves multiple entities and flushes pending changes. |
Batch-oriented workflows requiring a flush. |
A flush synchronizes pending persistence changes with the database, but it does not necessarily commit the surrounding transaction. Do not use saveAndFlush() automatically for every operation; normal transaction handling is usually sufficient.
20. Transaction Management During CRUD Operations
Transactions ensure that related database operations are handled as a logical unit. If an operation fails, Spring's transaction infrastructure can roll back changes according to the configured rollback rules.
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class EnrollmentService {
private final StudentRepository repository;
public EnrollmentService(StudentRepository repository) {
this.repository = repository;
}
@Transactional
public Student registerStudent(Student student) {
return repository.save(student);
}
@Transactional(readOnly = true)
public long countStudents() {
return repository.count();
}
}
Important points:
- Use transactions around related database operations that must succeed or fail together.
- Use
readOnly = true for methods intended to read data; this is generally a hint, not a universal write-prevention guarantee.
- Understand the default rollback behavior for unchecked and checked exceptions.
- Avoid holding transactions open while waiting for slow external services.
- Handle constraint violations at an appropriate application boundary.
21. Common CRUD Errors and Solutions
| Error |
Possible Cause |
Solution |
| Not a managed type |
Entity is not discovered or lacks @Entity. |
Check entity annotations, package structure, and scanning configuration. |
| Table does not exist |
Database schema is missing or table mapping is incorrect. |
Verify the database and apply the required schema migration. |
| Duplicate key exception |
An existing record conflicts with a primary-key or unique constraint. |
Validate input and handle constraint violations appropriately. |
| Student not found |
The requested ID is not present. |
Return a meaningful not-found response, commonly HTTP 404. |
| Null field inserted |
Required request fields were missing or not validated. |
Add request validation and enforce required database constraints. |
| Update loses existing values |
A new entity was created with only some fields populated. |
Retrieve the existing entity and update only the intended fields. |
| LazyInitializationException |
A lazy relationship is accessed outside an appropriate persistence context. |
Load required data within a suitable transaction or use an intentional fetch strategy. |
| Slow list endpoint |
Too many records are loaded at once or queries are inefficient. |
Use pagination, appropriate indexes, and query optimization. |
| Database connection failure |
Incorrect URL, credentials, driver, or database availability. |
Check connection properties, driver dependencies, and database status. |
22. CRUD Best Practices
- Use separate controller, service, and repository layers.
- Keep business rules in the service or domain layer.
- Validate incoming request data before persistence.
- Use DTOs to separate public API contracts from database entities.
- Retrieve an existing entity before changing selected fields.
- Use pagination for large collections.
- Define clear behavior for missing records and duplicate values.
- Use transactions when multiple operations must be coordinated.
- Keep database credentials out of source code.
- Use migration tools to manage production schema changes.
- Do not expose stack traces, SQL details, or sensitive database information in API responses.
- Test repository methods and API behavior with realistic data.
- Review cascading and entity relationships before deleting records.
- Use database constraints as a final layer of data integrity protection.
23. Practical Exercise: Student Management CRUD Application
Build a Student Management REST API using Spring Boot, Spring Data JPA, and MySQL.
Project Requirements
- Create a database named
training_db.
- Configure the database connection in
application.properties.
- Create a Student entity with ID, name, email, and course fields.
- Create a StudentRepository extending
JpaRepository<Student, Long>.
- Implement service methods for creating, reading, updating, and deleting students.
- Create REST endpoints using POST, GET, PUT, and DELETE.
- Validate student names, emails, and required fields.
- Handle missing records and duplicate email addresses.
- Test the endpoints using Postman or curl.
- Add pagination and sorting to the list endpoint.
- Verify that changes persist in the database.
Expected Outcome
After completing the project, learners should be able to implement a complete CRUD workflow, connect a Spring Boot application to a database, use repository methods effectively, and handle common persistence errors.
24. Interview Questions and Answers
Q1. What does CRUD stand for?
CRUD stands for Create, Read, Update, and Delete. These are the four basic operations used to manage persistent data.
Q2. What is JpaRepository?
JpaRepository is a Spring Data JPA interface that provides CRUD operations along with paging, sorting, and additional JPA-specific functionality.
Q3. How do you insert a record using JpaRepository?
Use the save() method with a new entity instance. The JPA provider manages the persistence operation.
Q4. How do you retrieve a record by its ID?
Use findById(id), which returns an Optional representing whether a matching record exists.
Q5. How do you update an existing record?
Retrieve the existing entity, modify the required fields within an appropriate transaction, and allow JPA to synchronize the changes. The repository's save() method may also be used where appropriate.
Q6. How do you delete a record by ID?
Use deleteById(id) or retrieve the entity and call delete(entity). Choose the approach based on the application's required not-found behavior.
Q7. What is the difference between findById() and getReferenceById()?
findById() retrieves an entity result wrapped in an Optional. getReferenceById() obtains a reference and may defer checking whether the record exists until the reference is accessed.
Q8. What is the purpose of saveAndFlush()?
It saves an entity and flushes pending persistence changes to the database immediately, but does not necessarily commit the surrounding transaction.
Q9. Why should DTOs be used in CRUD APIs?
DTOs separate the public request and response formats from persistence entities, allowing better validation, controlled field exposure, and more stable API contracts.
Q10. How should an API handle a missing record?
The service can throw a dedicated not-found exception, and a global exception handler can convert it into an HTTP 404 response.
Q11. Why is pagination important?
Pagination limits the number of records returned per request, improving response size and helping reduce memory and database load.
Q12. What is the role of @Transactional?
It defines a transaction boundary so related database operations can be coordinated and rolled back according to the configured rules.
Q13. Does save() always execute an SQL INSERT?
No. The persistence provider determines the necessary operation based on entity state, identifier handling, and the persistence context. A save operation may result in an insert or an update-related operation.
Q14. How do you handle duplicate emails?
Validate the input where appropriate, enforce a unique constraint in the database, and translate the resulting constraint violation into a suitable API response.
Q15. How can CRUD performance be improved?
Use pagination, appropriate indexes, efficient queries, controlled fetch strategies, suitable connection-pool settings, and carefully designed transactions.
25. Quick Revision Checklist
- Understand Create, Read, Update, and Delete operations.
- Create a JPA entity and a JpaRepository interface.
- Use
save() for persistence operations.
- Use
findById(), findAll(), and derived query methods for retrieval.
- Retrieve existing entities before updating selected fields.
- Use
delete() or deleteById() for deletion.
- Build REST endpoints with appropriate HTTP methods and status codes.
- Handle validation errors, missing records, and uniqueness conflicts.
- Understand transaction boundaries and persistence-context behavior.
- Use pagination, sorting, DTOs, and database constraints appropriately.
26. Conclusion
Performing CRUD operations using JpaRepository is a fundamental skill for developing database-backed Spring Boot applications. Spring Data JPA reduces repetitive persistence code and provides standard methods for saving, retrieving, updating, and deleting entities.
By combining entity mapping, repository interfaces, service-layer business logic, REST controllers, transaction management, validation, and exception handling, developers can build maintainable APIs for real-world applications.
To explore more backend development topics, visit the Advanced Java Training course at JustAcademy, register through the course demo registration page, or browse the JustAcademy website.