Foundation · Build

Projections, Specifications & Auditing

Build efficient read models, compose optional search filters, and capture creation/update timestamps without exposing managed entities.

Primary source: Spring Data JPA: Projections and DTO Read Models

30 min

Lesson 16 of 17

1d streak

The Architectural Question

Loading full entity graphs for list screens wastes memory and couples API responses to the persistence model, while ad-hoc string query combinations become impossible to maintain.

What this guide proves

  • DTO Projections
  • JpaSpecificationExecutor
  • JPA Auditing

Read models: Decoupling query views from write entities

Using JPA entities for read-heavy operations is a performance hazard: entities attach to the Persistence Context, consume heap memory, and trigger dirty checking comparisons. Read models decouple queries into lightweight DTOs and database views.

DimensionWrite Model (JPA Entity)Read Model (Projection / DTO)
StateManaged, mutable, attached to L1 cacheDetached, immutable Java Record
Dirty CheckingYes (generates diff snapshots)No (zero dirty checking overhead)
AssociationsComplex object graphs, proxy referencesFlattened tabular projections
SecurityMay accidentally expose internal columnsExposes strictly the requested public fields

Interface-based vs Class-based DTO projections

OrderProjections.java
// Fast class-based Record Projection
public record OrderSummaryView(UUID id, String customerEmail, double total) {}

public interface OrderRepository extends JpaRepository<OrderEntity, UUID> {
    @Query("SELECT new com.example.orders.dto.OrderSummaryView(o.id, o.customerEmail, o.totalAmount) FROM OrderEntity o WHERE o.status = :status")
    List<OrderSummaryView> findSummariesByStatus(String status);
}

Active Recall Checkpoint

Why are Java 21 Record DTO projections preferred over dynamic interface-based projections in high-throughput endpoints?

Hint: Consider JDK dynamic proxy overhead.

Show answer & explanation

Spring Data implements interface projections using JDK dynamic proxies, which incurs small runtime reflection overhead per row. Java Record DTOs are instantiated directly by the query without proxy overhead.

Read model guidelines

Use it when

Use constructor DTO projections or database views for all search, dashboard, and listing endpoints.

Reconsider when

Load full JPA entities only when mutating business state that must be saved back to the database.

Runtime Architecture & Flow

Decision canvas

Evaluate the choice across three constraints

These are comparison criteria, not sequential steps. A strong answer states which constraint dominates in the current system.

CRITERION 01

DTO Projections

Ask how each option changes this guarantee, its operational cost, and its failure mode.

CRITERION 02

JpaSpecificationExecutor

Ask how each option changes this guarantee, its operational cost, and its failure mode.

CRITERION 03

JPA Auditing

Ask how each option changes this guarantee, its operational cost, and its failure mode.

Decision prompt: Add a lightweight `ProductSummary` projection, a composable catalog search specification, and created/updated timestamps to the product catalog.

Synthesis & Production Verification

Deliberate Failure Condition

  1. Break: Return a Page<Product> directly from the controller and serialize a lazy relationship
  2. Observe: Unplanned SQL, unstable JSON, or LazyInitializationException
  3. Repair: Select the response shape deliberately and map it inside the transaction instead of treating entities as API DTOs.

Production Acceptance Criteria

  • Return a projection containing only list-screen fields
  • Compose optional filters without one repository method per combination
  • Populate audit timestamps automatically

Architectural & Technical Review

Core Interview Questions

Q01Why should read-only endpoints avoid loading managed JPA entities?Show answer

Managed entities consume heap for snapshot tracking and dirty checking comparisons; DTO records are lightweight and immutable.

Q02What is the benefit of constructor DTO projections over interface projections?Show answer

Constructor projections instantiate concrete record instances directly, bypassing JDK dynamic proxy overhead per row.

Q03How do read models improve API security?Show answer

They expose strictly the requested fields, preventing accidental serialization of sensitive entity attributes.

Production Pitfalls

Critical Anti-Patterns

  • Pitfall: Serializing managed entities directly to JSON clients — Exposing entities leaks internal database columns and triggers LazyInitializationExceptions when touching uninitialized proxies.
  • Pitfall: Creating interface projections with heavy computed getters — SpEL expressions in interface projection getters execute per row in memory, slowing down pagination results.

Primary Documentation & Specifications