Tutorial

264 posts in this section

Entity Relationships: @OneToMany, @ManyToOne, @ManyToMany

Relationships are the trickiest part of JPA. A wrong cascade type or a missing mappedBy causes subtle bugs that appear in production. This article covers every relationship type with real examples and the pitfalls to avoid. Relationship Fundamentals JPA relationships can be: Direction: Unidirectional (one side knows about the other) or Bidirectional (both sides know each other) Cardinality: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany Fetch: LAZY (load on access) or EAGER (load immediately) Ownership: The side with the foreign key column is the owner Default fetch types:

Continue reading »

Externalized Configuration with @ConfigurationProperties

@ConfigurationProperties binds external configuration to a typed Java class — replacing scattered @Value annotations with a single, validated, testable object. Why @ConfigurationProperties Over @Value // @Value — scattered, no type safety, no validation @Service public class PaymentService { @Value("${payment.gateway.url}") private String gatewayUrl; @Value("${payment.gateway.timeout:5000}") private int timeoutMs; @Value("${payment.gateway.api-key}") private String apiKey; @Value("${payment.gateway.max-retries:3}") private int maxRetries; } // @ConfigurationProperties — one place, typed, validated @Service public class PaymentService { private final PaymentProperties properties; // all config in one place, injected as a single object } @ConfigurationProperties gives you:

Continue reading »

Full Observability: Prometheus + Grafana + Tempo + Loki

Observability means being able to answer “what’s wrong and why” from the outside — without modifying the code. The three pillars: metrics (what happened), logs (what the code did), and traces (how a request flowed). This article wires them all together. The Stack Spring Boot App ├── Metrics → Micrometer → Prometheus scrape → Grafana dashboards ├── Traces → Micrometer Tracing → OTLP → Tempo → Grafana trace view └── Logs → Logback → Loki4j → Loki → Grafana log explorer All three converge in Grafana — click a metric spike to see the correlated logs and traces for that exact time window.

Continue reading »

Global Exception Handling with @ControllerAdvice and ProblemDetail

Without global exception handling, Spring returns raw stack traces or inconsistent error shapes. This article shows how to centralize all error handling in one place and return structured, RFC 7807-compliant responses. The Problem Without Global Handling Default Spring Boot error responses are inconsistent: // Validation failure (MethodArgumentNotValidException) { "timestamp": "2026-05-03T10:00:00.000+00:00", "status": 400, "error": "Bad Request", "path": "/api/orders" } // Details of which fields failed? Not included. // Custom exception not handled // → 500 Internal Server Error with a stack trace in the body (in dev mode) What clients actually need:

Continue reading »

GraalVM Native Images: Millisecond Startup

A regular Spring Boot application takes 2–10 seconds to start. A GraalVM native image of the same application starts in under 100 milliseconds. For serverless functions, batch jobs, and CLI tools, this is the difference between viable and unusable. What Is a Native Image? GraalVM’s native image compiler performs ahead-of-time (AOT) compilation. Instead of shipping a JAR that the JVM interprets at runtime, you ship a standalone executable that: Contains only the code your application actually uses Has no JVM startup overhead Uses much less memory (no JIT compiler, no class metadata) Starts in milliseconds The tradeoff: compile time increases from seconds to minutes.

Continue reading »

Graceful Shutdown and Production Readiness

An application that starts and serves traffic is not production-ready. Production readiness means it shuts down cleanly, handles spikes, recovers from transient failures, and gives you visibility into what it’s doing. This article covers the operational layer. Graceful Shutdown When Kubernetes terminates a pod, it sends SIGTERM. Without graceful shutdown, in-flight requests are killed mid-execution — users see 500 errors or dropped writes. Enable graceful shutdown: server: shutdown: graceful # wait for in-flight requests to complete spring: lifecycle: timeout-per-shutdown-phase: 30s # max wait before forcing shutdown With this configured, Spring Boot:

Continue reading »

Handling Requests: Path Variables, Query Params, and Request Bodies

A REST API receives data in many places: URL path, query string, body, headers, cookies. This article covers every way to extract that data in Spring MVC. @PathVariable — Extract from URL Use @PathVariable when the data is part of the URL path: // URL: GET /api/orders/550e8400-e29b-41d4-a716-446655440000 @GetMapping("/{id}") public OrderResponse getOrder(@PathVariable UUID id) { return orderService.findById(id) .map(OrderResponse::from) .orElseThrow(() -> new OrderNotFoundException(id)); } Spring automatically converts the string segment to the parameter type (UUID, Long, int, etc.

Continue reading »

How Spring Boot Auto-Configuration Works — The Magic Explained

Spring Boot “just works” — you add a dependency and things appear in your application context. This article explains exactly how that happens, so you can debug it when it doesn’t, and extend it when you need to. What Auto-Configuration Actually Does When you add spring-boot-starter-data-jpa to your project, you don’t write a single line of config — yet Spring creates a DataSource, EntityManagerFactory, and JpaTransactionManager automatically. That’s auto-configuration. Auto-configuration is a set of @Configuration classes that Spring Boot ships.

Continue reading »

Integration Testing with @SpringBootTest and Testcontainers

Integration tests verify that all layers work together — HTTP → controller → service → repository → database. This article shows how to write them efficiently with Testcontainers and manage test isolation. @SpringBootTest — The Full Context @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class OrderIntegrationTest { // Loads the FULL Spring ApplicationContext // Everything: controllers, services, repositories, security // Starts on a random port (avoids port conflicts when running tests in parallel) } WebEnvironment options Option What it starts Use for RANDOM_PORT Embedded server on random port Full HTTP round-trip tests DEFINED_PORT Embedded server on configured port When you need a fixed port MOCK (default) No real server, MockMvc available Fast tests without real HTTP NONE No server at all Service/repo tests only Testcontainers — Real Database <dependency> <groupId>org.

Continue reading »

Inter-Service Communication with OpenFeign and RestClient

Services call each other over HTTP. You can use raw RestClient with a URL, or you can use OpenFeign — a declarative HTTP client that turns an interface into a fully functional HTTP client. This article covers both. OpenFeign: Declarative HTTP Client <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> @SpringBootApplication @EnableFeignClients public class OrderServiceApplication { } Defining a Feign Client @FeignClient( name = "inventory-service", // service name — resolved via Eureka path = "/api/inventory" ) public interface InventoryClient { @GetMapping("/products/{productId}/availability") InventoryResponse checkAvailability( @PathVariable UUID productId, @RequestParam int quantity ); @PostMapping("/reservations") ReservationResponse reserve(@RequestBody ReservationRequest request); @DeleteMapping("/reservations/{reservationId}") void cancelReservation(@PathVariable UUID reservationId); @GetMapping("/products") Page<ProductResponse> findProducts( @RequestParam String category, @SpringQueryMap Pageable pageable // Pageable as query params ); } Using it — just inject and call:

Continue reading »