Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

JPQL queries

find loads one entity by its id. For everything else — "all posts with more than 50 views", "the number of posts" — there is JPQL (Jakarta Persistence Query Language). It looks like SQL, but works with entities and their fields instead of tables and columns. Hibernate translates a JPQL query into the SQL of your database. Hibernate's own extended version is called HQL; every JPQL query is also valid HQL.

The first query

The examples use the Seed data from the lesson on lazy loading: three posts with 120, 80 and 45 views.

// src/main/java/com/example/blog/Main.java
Seed.run(emf);
emf.runInTransaction(em -> {
    List<Post> popular = em.createQuery(
                    "select p from Post p where p.views > :min order by p.views desc", Post.class)
            .setParameter("min", 50)
            .getResultList();
    popular.forEach(p -> IO.println(p.getTitle() + " (" + p.getViews() + ")"));
});
Hibernate: select p1_0.id,p1_0.author_id,p1_0.title,p1_0.views from Post p1_0 where p1_0.views>? order by p1_0.views desc
Hello, Hibernate (120)
Records in Java 25 (80)
PartMeaning
select p from Post pPost is the entity class name, p an alias
p.viewsA field of the entity, not a column
:minA named parameter; its value is set with setParameter
order by p.views descSorting, as in SQL

The second argument of createQuery is the result type. With it you get a TypedQuery<Post> and a List<Post> without casts.

One value

// src/main/java/com/example/blog/Main.java
Long total = em.createQuery("select count(p) from Post p", Long.class)
        .getSingleResult();
IO.println("Total posts: " + total);
Hibernate: select count(p1_0.id) from Post p1_0
Total posts: 3

count returns Long. getSingleResult expects exactly one row.

A result that may be missing

When the row may not exist, use getSingleResultOrNull, added in Jakarta Persistence 3.2:

// src/main/java/com/example/blog/Main.java
Post found = em.createQuery("select p from Post p where p.title = :title", Post.class)
        .setParameter("title", "Lazy loading")
        .getSingleResultOrNull();
IO.println("Found: " + found.getTitle());
 
Post missing = em.createQuery("select p from Post p where p.title = :title", Post.class)
        .setParameter("title", "No such post")
        .getSingleResultOrNull();
IO.println("Missing: " + missing);
Found: Lazy loading
Missing: null

Why parameters

Never glue values into the query text with +. A parameter is passed to the database separately, so a title like ' or 1=1 -- stays just text and cannot change the query — this protects against SQL injection, the same way PreparedStatement does in JDBC.

Common mistake

JPQL uses entity and field names, not table and column names. If the class Post is mapped to the table posts, the query select p from posts p fails:

java.lang.IllegalArgumentException: org.hibernate.query.sqm.UnknownEntityException: Could not resolve root entity 'posts'

The same happens with a typo in a field name:

java.lang.IllegalArgumentException: org.hibernate.query.sqm.UnknownPathException: Could not resolve attribute 'titel' of 'com.example.blog.Post' [select p from Post p where p.titel = 'x']

And getSingleResult without a matching row throws jakarta.persistence.NoResultException: No result found for query [...] — that is what getSingleResultOrNull is for.

Thanks for reading!

Donate to the site — we work hard for you.