Validation, Error Handling & Testing
Validation & Global Error Handling
public record CreateUserRequest(
@NotBlank String name,
@Email String email
) {}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
return ResponseEntity.badRequest().body(new ErrorResponse("Validation failed", ex.getMessage()));
}
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(UserNotFoundException ex) {
return ResponseEntity.status(404).body(new ErrorResponse("Not found", ex.getMessage()));
}
}
// Centralizes error formatting — no repeated try/catch in every controllerAvoiding N+1 Queries
// N+1 problem: 1 query for orders, then N more for each order.customer
// (lazy loading triggered inside a loop)
for (Order order : orderRepository.findAll()) {
System.out.println(order.getCustomer().getName()); // separate query per order!
}
// Fix — JOIN FETCH pulls the association in the SAME query
@Query("SELECT o FROM Order o JOIN FETCH o.customer")
List<Order> findAllWithCustomer();Testing Slices
// Fast — loads only the web layer, service is mocked
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired MockMvc mockMvc;
@MockBean UserService userService;
@Test
void getUser_returnsUserJson() throws Exception {
when(userService.findById(1L)).thenReturn(new UserDto(1L, "Alice"));
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Alice"));
}
}
// Loads only JPA-related beans against a test database
@DataJpaTest
class UserRepositoryTest {
@Autowired UserRepository userRepository;
@Test
void findByEmail_returnsMatchingUser() {
userRepository.save(new User("a@example.com", "Alice"));
assertThat(userRepository.findByEmail("a@example.com")).isPresent();
}
}
// Full context — closest to a real integration test, slowest to start
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserApiIntegrationTest { /* ... */ }Actuator
# application.yml
management:
endpoints:
web:
exposure:
include: health,metrics,info
# GET /actuator/health — wired into Kubernetes liveness/readiness probes
# GET /actuator/metrics — JVM, HTTP, and custom application metricsKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free