Unravel โบ Java concepts โบ Persistence and JPA
JPQL, native queries and the Criteria API
JPQL is SQL written against your entity classes and fields instead of tables and columns.
Learn these first
At a glance
JPQL is asking in the language the object world speaks ("orders whose customer.city is Paris"), and JPA translates it into the table language for you.
- 1em.createQuery("select o from Order o where o.status = :status", Order.class) builds a typed query; setParameter("status", s) binds the value; getResultList() runs it
- 2Use setMaxResults and setFirstResult for paging, join fetch to load a relation in the same query, and constructor expressions (select new com.shop.OrderSummary(o.id, o.total) ...) to select exactly the fields a DTO needs
- 3Named queries (@NamedQuery) are checked at startup, which catches typos earlyNative queries (createNativeQuery) allow database-specific SQL, with results mapped by @SqlResultSetMapping or to entities.
- 4The Criteria API (CriteriaBuilder, CriteriaQuery, Root) constructs the same thing programmatically, useful when filters are optional, at the cost of readability
- getSingleResult() throws NoResultException or NonUniqueResultException
- Never build JPQL by concatenating user input; use named parameters
- The query string is parsed into an abstract syntax tree (HQL parser), translated to SQL using the entity mappings and the dialect, and cached as a query plan keyed by the string
List<OrderSummary> rows = em.createQuery( "select new com.shop.OrderSummary(o.id, o.total) " + "from Order o where o.status = :status order by o.createdOn desc", OrderSummary.class) .setParameter("status", status) .setMaxResults(50) .getResultList();- line 1 uses createQuery, the core of JPQL, native queries and the Criteria API
- line 4 uses setParameter, the core of JPQL, native queries and the Criteria API
- line 5 uses setMaxResults, the core of JPQL, native queries and the Criteria API
- line 6 uses getResultList, the core of JPQL, native queries and the Criteria API
Read the full explanation (about 1 min)
JPQL is SQL written against your entity classes and fields instead of tables and columns. The Criteria API builds the same queries with code objects, and native queries drop to plain SQL when needed.
A mental model
JPQL is asking in the language the object world speaks ("orders whose customer.city is Paris"), and JPA translates it into the table language for you.
How it works
em.createQuery("select o from Order o where o.status = :status", Order.class) builds a typed query; setParameter("status", s) binds the value; getResultList() runs it.
Use setMaxResults and setFirstResult for paging, join fetch to load a relation in the same query, and constructor expressions (select new com.shop.OrderSummary(o.id, o.total) ...) to select exactly the fields a DTO needs.
Named queries (@NamedQuery) are checked at startup, which catches typos early. Native queries (createNativeQuery) allow database-specific SQL, with results mapped by @SqlResultSetMapping or to entities.
The Criteria API (CriteriaBuilder, CriteriaQuery, Root) constructs the same thing programmatically, useful when filters are optional, at the cost of readability.
Going deeper
getSingleResult() throws NoResultException or NonUniqueResultException. Prefer getResultStream().findFirst() or a repository returning Optional.
Never build JPQL by concatenating user input; use named parameters. Concatenation allows injection exactly like plain SQL.
Fetching a collection with join fetch and paging together makes Hibernate load everything into memory and page in Java (warning HHH000104). Page the parents first and fetch children in a second query.
Under the hood
The query string is parsed into an abstract syntax tree (HQL parser), translated to SQL using the entity mappings and the dialect, and cached as a query plan keyed by the string. Parameters are bound as JDBC parameters, which is also what prevents injection.
Common mistakes
- String concatenation of user input.
- Fetch-join of a collection combined with paging.
- Selecting whole entities when a projection would do.
- Assuming native queries are portable.
How Unravel recognises it
When you paste code, Unravel looks for these signs of jpql, native queries and the criteria api. Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@NamedQuery@NamedQueries@NamedNativeQuery@SqlResultSetMapping - Types:
CriteriaBuilderCriteriaQueryTypedQueryNativeQueryStoredProcedureQuery - Method calls:
createNamedQuery()createNativeQuery()em.createQuery()entityManager.createQuery()getResultList()getSingleResult()setParameter()setMaxResults()setFirstResult()
Part of a learning path
- JPA and Hibernate in depth ยท previous: Persistence context, flush and dirty checking ยท next: Dynamic queries (Specifications, Example, Querydsl)
Related concepts
Frequently asked questions
What is JPQL, native queries and the Criteria API?
JPQL is SQL written against your entity classes and fields instead of tables and columns. The Criteria API builds the same queries with code objects, and native queries drop to plain SQL when needed.
What are the most common mistakes with JPQL, native queries and the Criteria API?
String concatenation of user input. Fetch-join of a collection combined with paging. Selecting whole entities when a projection would do. Assuming native queries are portable.
How can I see JPQL, native queries and the Criteria API in real code?
Paste your own Java into Unravel and it will point out where jpql, native queries and the criteria api appears, explain how it works at the detail level you choose, and draw the flow of the code around it.