Spring Boot + JPA Patterns
Backend knowledge base for Java 21+ services built on Spring Boot 3 with PostgreSQL and Redis. Covers layered architecture, persistence with Spring Data JPA + Hibernate, Flyway-managed schema, HikariCP tuning, Spring Security 6, REST design, the JUnit 5 + Mockito + Testcontainers test stack, and Micrometer-based observability. Designed to be auto-loaded by java-engineer and referenced by reviewers and architects working on Java services.
Project Architecture (Spring Boot 3)
Use layered or hexagonal architecture consistently across the project:
- Controller (
@RestController): HTTP mapping, request validation, response DTOs - Service (
@Service,@Transactional): business logic, orchestration, transaction boundaries - Repository (
@Repository, extendsJpaRepository): data access, custom queries - Entity (
@Entity): JPA-managed domain objects, lifecycle callbacks - DTO / Record: data transfer between layers — never expose entities in API responses
- Mapper: convert between entities and DTOs (MapStruct or manual)
- Config (
@Configuration): bean definitions, property binding, infrastructure setup - Exception (
@ControllerAdvice): global error handling with@ExceptionHandler
Dependency injection rules:
- Constructor injection only.
finalfields with@RequiredArgsConstructoror explicit constructor. - No field injection (
@Autowiredon fields). - Java 21+ features: records, sealed classes, pattern matching, text blocks, virtual threads. No raw types or unchecked casts.
Configuration:
- Use
application.ymlover.properties— structured, profile-aware. - Profile-based config:
application-dev.yml,application-prod.yml,application-test.yml. - Externalize environment values:
${DB_URL},${REDIS_HOST},${JWT_SECRET}. @ConfigurationProperties+@Validatedfor type-safe binding.- Virtual threads:
spring.threads.virtual.enabled=truefor I/O-bound workloads (Java 21+).
Spring Data JPA + Hibernate
Entity design:
@Entitywith explicit@Table(name = "...")— never rely on naming strategy alone.- Primary keys:
@GeneratedValue(strategy = IDENTITY)for serial,@UuidGeneratorfor UUID. - Auditing:
@CreatedDate,@LastModifiedDatevia@EntityListeners(AuditingEntityListener.class). @Column(nullable = false)matches DB constraints.equals()/hashCode()based on business key or ID — never on all fields.@Enumerated(EnumType.STRING)— neverEnumType.ORDINAL.
Query and fetch patterns:
- N+1 prevention:
@EntityGraphorJOIN FETCHin JPQL for association loading. - Lazy by default: all
@ManyToOneand@OneToManyuseFetchType.LAZY. - Projections: interface/DTO projections for read-only queries — avoid loading full entities.
- Batch ops:
hibernate.jdbc.batch_size=25for bulk inserts/updates. - Read-only transactions:
@Transactional(readOnly = true)enables Hibernate optimizations. - Indexes: create for columns in WHERE, JOIN, ORDER BY via Flyway migration.
- Avoid native SQL except for performance-critical paths, with a comment explaining why.
Flyway Migrations
- Location:
src/main/resources/db/migration/. - Versioned:
V{version}__{description}.sql(e.g.,V1__create_users_table.sql). - Repeatable:
R__{description}.sqlfor views, functions, seed data. - Each migration is idempotent where possible (
CREATE INDEX IF NOT EXISTS,ALTER TABLE ... ADD COLUMN IF NOT EXISTS). - Never modify or delete existing migrations.
- Never use
ddl-auto=updatein production — Flyway owns schema. - CI test plan: run migrations against an empty DB and from the previous version.
HikariCP Connection Pool
- Configure
maximum-pool-size,connection-timeout,max-lifetime. - Initial pool sizing heuristic:
(2 * CPU cores) + number_of_disks— tune via load testing. - Set
max-lifetimebelow PostgreSQL'sidle_in_transaction_session_timeout. - Monitor via Micrometer:
hikaricp.connections.active,hikaricp.connections.pending. - Detect connection leaks via
leakDetectionThresholdin non-prod profiles.
Redis Caching
Declarative caching:
@EnableCaching+@Cacheable,@CachePut,@CacheEvict.- Spring Data Redis with Lettuce client (default in Spring Boot 3).
- Cache key design:
{entity}:{id}or{entity}:list:{hash_of_params}— predictable, eviction-friendly. - TTL: configured per cache in
RedisCacheConfiguration. - Eviction:
@CacheEvicton mutations;allEntries = truefor list caches. - Serialization:
GenericJackson2JsonRedisSerializer— human-readable, debuggable.
Patterns:
- Cache-aside: default
@Cacheableflow — check cache, miss → load from DB → store. - Distributed locks:
RedisTemplate+ Lua scripts, or Redisson. - Rate limiting: Redis sliding-window counters.
- Session storage: Spring Session with Redis for stateless horizontal scaling.
- Never cache mutable state without an explicit invalidation strategy.
- Monitor hit/miss ratio, eviction rate, memory usage (Redis INFO + Micrometer).
Spring Security
- Bean-based config via
SecurityFilterChain— never extendWebSecurityConfigurerAdapter(removed in Spring Security 6). - JWT auth: stateless sessions, token validation in
OncePerRequestFilter. - OAuth2 resource server:
spring-boot-starter-oauth2-resource-serverfor JWT/opaque tokens. - Method-level:
@PreAuthorize("hasRole('ADMIN')"),@Securedfor role-based access. - CORS: configure explicitly in
SecurityFilterChain— never@CrossOrigin("*"). - CSRF: disable only for stateless REST (
csrf(csrf -> csrf.disable())); keep enabled for session apps. - Passwords:
BCryptPasswordEncoder. Never store plaintext. - Validate all inputs; rely on JPA's parameterized queries (no string concat into JPQL).
REST API Design
- Resource-oriented URLs:
/api/v1/users,/api/v1/users/{id}/orders. - HTTP verbs: GET (read), POST (create), PUT (full update), PATCH (partial), DELETE (remove).
- Status codes: 200, 201, 204, 400, 401, 403, 404, 409, 422, 500 — precise, never generic 200 for errors.
- Pagination:
Pageableparameter →Page<T>withpage,size,sort. - Versioning: URL-based (
/api/v1/...). - Validation:
@Valid+ Jakarta Bean Validation (@NotNull,@Size,@Email,@Pattern). - Error responses: RFC 7807 problem-details shape —
{ timestamp, status, error, message, path, details[] }. - Custom exception hierarchy:
BusinessException(4xx) → specific subclasses;SystemException(5xx). - Global
@ControllerAdvicewith@ExceptionHandlerper exception family. - Never expose stack traces or internal details in API responses.
- Log exceptions with correlation IDs.
- OpenAPI: springdoc-openapi for generated docs; keep schemas in sync with DTOs.
Testing
JUnit 5
- Structure: Given → When → Then.
- AssertJ assertions;
@DisplayNamefor readability. - Skip tests for framework behavior, getters/setters, generated code.
Mockito
@Mock+@InjectMocks,@ExtendWith(MockitoExtension.class)for unit tests.- Mock at architectural seams (services depend on repositories; controllers depend on services).
- Verify behavior with
verify(...)only when interaction matters; otherwise assert state.
Spring test slices
@WebMvcTest+MockMvcfor controllers.@DataJpaTestfor repositories (paired with Testcontainers PostgreSQL).@DataRedisTestfor Redis-only slices.@SpringBootTest+MockMvcfor end-to-end request-response cycles.
Testcontainers
@Testcontainers+@Containerfor PostgreSQL and Redis to match production runtimes.- Reuse containers across tests with
withReuse(true)and~/.testcontainers.propertiesfor local speed. - Lifecycle: containers start before Spring context; use
@DynamicPropertySourceto inject JDBC URL and Redis host.
Observability
- Logging: SLF4J + Logback. Structured JSON in production. MDC for correlation IDs.
- Metrics: Micrometer + Prometheus via
/actuator/prometheus. Track latency, error rate, DB pool, cache hit ratio. - Health:
/actuator/healthwith custom indicators for PostgreSQL, Redis, external services. - Tracing: Micrometer Tracing (Brave or OpenTelemetry bridge). Propagate trace/span IDs across services.
- Actuator: expose only necessary endpoints. Secure with a separate security config.
Performance
- Virtual threads (Java 21+): enable for I/O-bound ops. Prefer
ReentrantLockoversynchronizedto avoid pinning carriers. - Connection pool: monitor HikariCP metrics and adjust pool size per throughput/latency.
- Query optimization:
EXPLAIN ANALYZEon slow queries. Add indexes. Paginate large results. - Caching: cache frequently read, rarely changed data. Measure before caching.
- Async:
@Async+ customTaskExecutor.CompletableFuturefor parallel calls. - GraalVM native: consider for microservices needing fast startup.
When this applies
| Workflow | Apply this knowledge |
|---|---|
Agent(java-engineer) invocation |
Auto-loaded |
/develop with a Java work package |
Spawned developer agent loads this |
/code-review on Java PRs |
Reviewer references these patterns |
/architecture-design for a Java service |
Architect references API + persistence patterns |
/bugfix on a Spring/JPA defect |
Developer + QA reference fetch + transaction patterns |
Integration
- Consumed by:
java-engineer(primary),software-engineer(when reviewing Java code),db-engineer(JPA model and migration review),solution-architect(REST + service-layer design). - Companion knowledge:
owasp-coverageskill for Spring Security threat coverage;test-strategyskill for layered test pyramid;observability-methodsskill for Micrometer/OTel cross-stack patterns. - External references: Spring Boot 3 reference docs, Spring Data JPA docs, Hibernate ORM docs, JUnit 5 user guide, Testcontainers docs, HikariCP wiki, Flyway docs.