Solving the N+1 Problem in Spring Data JPA

The N+1 problem is a common performance issue in JPA. Here’s how to solve it in Spring Data JPA. Understanding N+1 Problem // N+1 queries: 1 for users + N for each user's posts List<User> users = userRepository.findAll(); for (User user : users) { List<Post> posts = postRepository.findByUserId(user.getId()); // N queries } Solutions 1. Eager Fetching @Entity public class User { @OneToMany(fetch = FetchType.EAGER) private List<Post> posts; } 2. Join Fetch (JPQL) @Query("SELECT u FROM User u JOIN FETCH u.posts") List<User> findAllWithPosts(); 3. Entity Graphs @EntityGraph(attributePaths = {"posts"}) @Query("SELECT u FROM User u") List<User> findAllWithPosts(); 4. Batch Fetching @Entity public class User { @BatchSize(size = 10) @OneToMany(fetch = FetchType.LAZY) private List<Post> posts; } Best Practices Use lazy loading by default Fetch associations when needed Use entity graphs Monitor query performance Use DTO projections Conclusion Solve N+1 problems with: ...

November 20, 2023 · 4431 views

Spring WebFlux: When to Use It and How to Build With It

Spring WebFlux enables reactive programming in Spring. Here’s when and how to use it. When to Use WebFlux Use WebFlux When: High concurrency requirements Non-blocking I/O needed Streaming data Microservices with reactive backends Use MVC When: Traditional request-response Blocking operations Existing Spring MVC codebase Simpler mental model needed Basic Setup Dependencies <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> Reactive Controller @RestController public class UserController { @GetMapping("/users") public Flux<User> getUsers() { return userService.findAll(); } @GetMapping("/users/{id}") public Mono<User> getUser(@PathVariable Long id) { return userService.findById(id); } @PostMapping("/users") public Mono<User> createUser(@RequestBody Mono<User> user) { return userService.save(user); } } Reactive Types Mono // Single value or empty Mono<String> mono = Mono.just("Hello"); Mono<String> empty = Mono.empty(); Mono<String> error = Mono.error(new RuntimeException()); Flux // Multiple values Flux<String> flux = Flux.just("A", "B", "C"); Flux<Integer> range = Flux.range(1, 10); Reactive Repository public interface UserRepository extends ReactiveCrudRepository<User, Long> { Flux<User> findByStatus(String status); Mono<User> findByEmail(String email); } Error Handling @GetMapping("/users/{id}") public Mono<User> getUser(@PathVariable Long id) { return userService.findById(id) .switchIfEmpty(Mono.error(new UserNotFoundException())) .onErrorResume(error -> { // Handle error return Mono.empty(); }); } Testing @SpringBootTest @AutoConfigureWebTestClient class UserControllerTest { @Autowired private WebTestClient webTestClient; @Test void shouldGetUsers() { webTestClient.get() .uri("/users") .exchange() .expectStatus().isOk() .expectBodyList(User.class); } } Best Practices Use reactive types throughout Avoid blocking operations Handle errors properly Use backpressure Monitor performance Conclusion Spring WebFlux provides: ...

October 15, 2023 · 4379 views