๐ Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
❌ Problem:
- Fetching unnecessary data from the database
- Increasing response time due to large joins
- N+1 query problem (multiple queries slowing down performance)
๐ก Solution: Use FetchType.LAZY to load only the necessary data when it's actually needed.
In this article, we’ll explore:
✅ What is FetchType.LAZY?
✅ Why EAGER fetching is bad for performance
✅ A complete example with one-to-many relationship
✅ How FetchType.LAZY reduces database queries
⚡ What is FetchType.LAZY in Hibernate?
In Hibernate, lazy loading (FetchType.LAZY) means that related entities are not fetched from the database until you explicitly access them.
๐ Example: Using FetchType.LAZY in One-to-Many Relationship
Let’s assume we have a Department and Employee relationship where a department has multiple employees.
1️⃣ Define Department Entity (One-to-Many Relationship)
@Entity
@Table(name = "departments")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY) // Lazy Loading
private List<Employee> employees;
// Constructors, Getters, and Setters
}
✅ FetchType.LAZY ensures that employees are not loaded until explicitly needed.
2️⃣ Define Employee Entity (Many-to-One Relationship)
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String role;
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
// Constructors, Getters, and Setters
}
❌ What Happens with FetchType.EAGER? (Bad Practice)
If we set fetch = FetchType.EAGER, Hibernate will always fetch all employees when retrieving a department.
@OneToMany(mappedBy = "department", fetch = FetchType.EAGER) // BAD PRACTICE
private List<Employee> employees;
๐จ Problem: Unnecessary Data Fetching
๐ Query Generated (EAGER Fetching)
SELECT * FROM departments d
LEFT JOIN employees e ON d.id = e.department_id
✅ Fetching ALL employees even if we don’t need them ๐จ
✅ Slows down performance when departments have thousands of employees
✅ Optimized API with FetchType.LAZY (Best Practice)
Now, let’s create a Spring Boot API that loads departments without employees (Lazy Loading).
3️⃣ Create a Repository Layer
public interface DepartmentRepository extends JpaRepository<Department, Long> {
}
4️⃣ Create a Service Layer (Lazy Loading in Action)
@Service
public class DepartmentService {
private final DepartmentRepository departmentRepository;
public DepartmentService(DepartmentRepository departmentRepository) {
this.departmentRepository = departmentRepository;
}
public List<Department> getAllDepartments() {
return departmentRepository.findAll(); // Employees are NOT fetched here
}
public Department getDepartmentById(Long id) {
return departmentRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Department not found"));
}
public List<Employee> getEmployeesByDepartment(Long departmentId) {
Department department = getDepartmentById(departmentId);
return department.getEmployees(); // Employees are only fetched here!
}
}
How Lazy Loading Works Here
1️⃣ getAllDepartments() - Fetching Departments Without Employees
public List<Department> getAllDepartments() {
return departmentRepository.findAll(); // Employees are NOT fetched here
}
๐ What happens here?
- The method fetches only department details.
- Employees are NOT loaded because
FetchType.LAZYis set in the@OneToManyrelationship. - Database Query (Generated by Hibernate):
SELECT * FROM departments; - Result: API returns only department details, keeping the response lightweight.
2️⃣ getDepartmentById() - Fetching a Single Department Without Employees
public Department getDepartmentById(Long id) {
return departmentRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Department not found"));
}
๐ What happens here?
- The method retrieves only the department from the database.
- Employees are NOT fetched at this stage.
- Database Query (Generated by Hibernate):
SELECT * FROM departments WHERE id = ?; - Result: Only department data is returned, avoiding unnecessary database calls.
3️⃣ getEmployeesByDepartment() - Fetching Employees When Needed
public List<Employee> getEmployeesByDepartment(Long departmentId) {
Department department = getDepartmentById(departmentId);
return department.getEmployees(); // Employees are only fetched here!
}
๐ What happens here?
- We first fetch the department details using
getDepartmentById(). - When we call
department.getEmployees(), Hibernate triggers a separate SQL query to load the employees. - Database Queries (Generated by Hibernate):
SELECT * FROM departments WHERE id = ?; -- Fetch department SELECT * FROM employees WHERE department_id = ?; -- Fetch employees only when needed - Result: Employees are loaded only when required, preventing unnecessary data fetching.
5️⃣ Create REST Controller for API Calls
@RestController
@RequestMapping("/api/departments")
public class DepartmentController {
private final DepartmentService departmentService;
public DepartmentController(DepartmentService departmentService) {
this.departmentService = departmentService;
}
// Fetch departments WITHOUT employees (Lazy Loading)
@GetMapping
public ResponseEntity<List<Department>> getAllDepartments() {
return ResponseEntity.ok(departmentService.getAllDepartments());
}
// Fetch employees of a department (Triggers Lazy Loading)
@GetMapping("/{id}/employees")
public ResponseEntity<List<Employee>> getEmployeesByDepartment(@PathVariable Long id) {
return ResponseEntity.ok(departmentService.getEmployeesByDepartment(id));
}
}
๐ Key Takeaways
✅ Use FetchType.LAZY for one-to-many and many-to-many relationships
✅ Avoid EAGER fetching unless absolutely necessary
✅ Lazy Loading reduces database load and improves API performance
✅ Load related entities only when needed (e.g., separate API call for employees)
By following these best practices, your Spring Boot applications will run faster and handle large datasets efficiently. ๐
Comments
Post a Comment
Leave Comment