Parameters & Named Queries
Parameters & Named Queries
Every practical JPQL query needs external values — a user ID to filter by, a status string to match, a date range to search within. Embedding those values directly as string literals is both insecure (SQL injection via JPQL) and inefficient (the persistence provider cannot reuse a compiled query plan). JPQL therefore provides two parameter-binding styles, and JPA complements them with the @NamedQuery mechanism that pre-declares queries at class load time.
Positional Parameters
Positional parameters are placeholders written as ?1, ?2, … (one-based). You bind values via TypedQuery.setParameter(int, Object).
Positional parameters are concise but fragile — if you reorder the WHERE clause you must also renumber every setParameter call. For anything beyond a one-liner, named parameters are almost always preferable.
Named Parameters
Named parameters use the colon prefix :name and are bound via setParameter(String, Object). The name is self-documenting and position-independent.
@NamedQuery.
Temporal and Special Types
When binding java.util.Date (legacy API) you had to supply a TemporalType hint. With the modern java.time API — LocalDate, LocalDateTime, Instant — Hibernate 6 maps them natively, so no hint is needed.
null to setParameter emits IS NULL in the generated SQL — you do not need a separate query branch for optional filters when you use the Criteria API, but with JPQL string queries you usually write explicit null-checks in the JPQL or use separate query methods.
Pagination: setFirstResult and setMaxResults
Pagination is controlled via TypedQuery fluently, not through SQL-dialect-specific clauses like LIMIT/OFFSET. This keeps the JPQL portable across databases.
Declaring Named Queries with @NamedQuery
A named query is a JPQL string that is declared once — on the entity class — and referenced by a logical name everywhere else. JPA parses and validates the JPQL at application start-up (when the EntityManagerFactory is built), not at runtime. This means syntax errors are caught on boot, not on the first user request.
Multiple named queries on the same entity use @NamedQueries({ @NamedQuery(...), @NamedQuery(...) }), or in Java 8+ you can simply repeat the annotation as shown above (repeatable annotation support).
Executing a Named Query
Call em.createNamedQuery(name, resultClass) instead of em.createQuery(jpqlString, resultClass). Parameter binding is identical.
EntityName.methodName (e.g., Order.findByStatus). Spring Data JPA looks for a named query by this convention automatically before generating a query from the method name, so the naming is doubly useful.
Named Queries in Spring Data JPA Repositories
If you use Spring Data JPA, a repository method named findByStatus on an Order repository automatically resolves the named query Order.findByStatus if it exists. You can also annotate the repository method with @Query for an inline alternative, but pre-declared named queries have the advantage of boot-time validation.
Performance Considerations
- Query plan caching: When you use parameter binding (both styles), the persistence provider compiles the JPQL once and caches the plan. Concatenating values directly into the JPQL string forces recompilation on every call and forfeits this cache entirely.
- Named queries are compiled once at startup: Their plan is reused on every call with no re-parsing overhead, making them the most efficient JPQL execution path.
- Use getSingleResult carefully: It throws
NoResultExceptionif no row matches andNonUniqueResultExceptionfor multiple rows. Wrap it or usegetResultList()and testisEmpty()if zero results are possible.
Summary
Use named parameters (:name) in preference to positional parameters for readability and resilience. Leverage setFirstResult/setMaxResults for database-portable pagination. Declare frequently executed or shared queries as @NamedQuery on the entity class to get boot-time syntax validation and the most efficient query-plan reuse. In the next lesson you will see these techniques combined with aggregate functions, grouping, and HAVING clauses.