Native Queries & Projections
Native Queries & Projections
JPQL covers the vast majority of everyday queries, but there are situations where you need to reach past the abstraction and write plain SQL: database-specific functions, window functions, complex recursive CTEs, or performance-critical queries that Hibernate's SQL generation cannot match. Spring Data JPA makes this straightforward with @Query(nativeQuery = true). At the same time, often you only need a slice of your entity — a handful of columns instead of the full object graph. Projections let you express that intent cleanly and carry measurable performance benefits.
When to Use Native Queries
Before reaching for native SQL, ask: can JPQL or a derived method express this? If yes, prefer it — JPQL is database-portable and works with Hibernate's first-level cache. Use native queries when you need:
- Vendor-specific syntax (
REGEXP_REPLACE,GENERATE_SERIES, window functions likeROW_NUMBER() OVER). - Recursive common table expressions (
WITH RECURSIVE). - Bulk
INSERT … SELECTorMERGEstatements. - Stored procedure calls where JPQL has no equivalent.
- Cases where the Hibernate-generated SQL is provably slower than a hand-tuned query after profiling.
entityManager.clear() or invalidate the affected cache region to avoid stale reads.
Writing a Native Query with @Query
Add nativeQuery = true to the annotation and write SQL exactly as your database expects it:
The return type List<Order> works because the query selects all columns (o.*) and Hibernate can map the result set to the Order entity. If your SQL selects only specific columns or aliases them differently, entity mapping fails — that is exactly the problem projections solve.
Named Native Queries (Alternative Placement)
For reusability or when you want to keep SQL out of repository interfaces, declare named native queries on the entity class with @NamedNativeQuery:
@SqlResultSetMapping: @SqlResultSetMapping is verbose and XML-era API. For new code, Spring Data interface projections (shown below) are far more concise and just as efficient.
Projections: The Problem They Solve
Consider an Order entity with 20 columns including a @Lob notes field and several @ManyToOne associations. A UI endpoint that only needs id, status, and totalAmount for a list view should not fetch all of that. Projections let you declare exactly which columns you want, and Hibernate generates a SELECT that fetches only those.
Interface Projections (Closed)
Define a Java interface with getter methods matching the entity property names. Spring Data generates a proxy at runtime:
When using @Query with a projection, alias each selected expression with the exact property name from the projection interface (AS id, AS totalAmount). Hibernate matches columns to getter methods by alias.
Interface Projections (Open) — SpEL Expressions
Open projections can combine or compute values using Spring Expression Language:
DTO (Class-Based) Projections
If you want a concrete, immutable DTO rather than a proxy, use a Java record (or a class) and a JPQL constructor expression:
JPQL's new expression calls the DTO constructor directly. The query selects only the three mapped columns — no proxy overhead, no reflection per getter call. This is the best choice when the DTO is serialized to JSON in a REST response, because Jackson works better with concrete types than with Hibernate proxy objects.
Projections with Native Queries
Interface projections also work with nativeQuery = true. Alias the SQL columns to match the projection getter names:
null, check that the SQL alias matches the getter name precisely (case-sensitive match against the camelCase getter, minus "get").
Performance Trade-offs at a Glance
- Full entity fetch: all columns loaded; entity tracked by the persistence context; ideal for update/delete operations.
- Closed interface projection: only selected columns fetched from DB; proxy wraps a tuple — slight CPU overhead per getter call, but major bandwidth win on wide entities.
- DTO (record/class) projection: only selected columns; constructor-mapped; no proxy; no persistence context tracking. Best for read-only API responses.
- Open interface projection: full entity loaded internally; SpEL evaluated; no column reduction benefit — use only when you need computed properties.
Summary
Native queries give you an escape hatch to raw SQL when JPQL cannot express what you need, while remaining fully integrated with Spring Data's repository model. Projections — whether interface-based or DTO-based — sharpen your queries to retrieve only the data your caller actually consumes, reducing both database I/O and object allocation. Combine them: a native aggregation query returning an interface projection is a clean, high-performance pattern for reporting endpoints. In the next lesson you will add pagination and sorting to these queries, enabling efficient list endpoints regardless of dataset size.