Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Запросы JPQL

find загружает одну сущность по id. Для всего остального — «все статьи больше чем с 50 просмотрами», «число статей» — есть JPQL (Jakarta Persistence Query Language). Он похож на SQL, но работает с сущностями и их полями, а не с таблицами и столбцами. Hibernate переводит запрос JPQL в SQL вашей базы. Расширенная версия языка у Hibernate называется HQL; любой запрос JPQL — заодно и корректный HQL.

Первый запрос

Примеры используют данные Seed из урока о ленивой загрузке: три статьи со 120, 80 и 45 просмотрами.

// 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
Привет, Hibernate (120)
Типы record в Java 25 (80)
ЧастьЗначение
select p from Post pPost — имя класса-сущности, p — псевдоним
p.viewsПоле сущности, а не столбец
:minИменованный параметр; значение задаёт setParameter
order by p.views descСортировка, как в SQL

Второй аргумент createQuery — тип результата. С ним вы получаете TypedQuery<Post> и List<Post> без приведений типов.

Одно значение

// src/main/java/com/example/blog/Main.java
Long total = em.createQuery("select count(p) from Post p", Long.class)
        .getSingleResult();
IO.println("Всего статей: " + total);
Hibernate: select count(p1_0.id) from Post p1_0
Всего статей: 3

count возвращает Long. getSingleResult ожидает ровно одну строку.

Результат, которого может не быть

Если строки может не оказаться, используйте getSingleResultOrNull, появившийся в 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", "Ленивая загрузка")
        .getSingleResultOrNull();
IO.println("Найдена: " + found.getTitle());
 
Post missing = em.createQuery("select p from Post p where p.title = :title", Post.class)
        .setParameter("title", "Такой статьи нет")
        .getSingleResultOrNull();
IO.println("Отсутствует: " + missing);
Найдена: Ленивая загрузка
Отсутствует: null

Зачем параметры

Никогда не вклеивайте значения в текст запроса через +. Параметр передаётся в базу отдельно, поэтому заголовок вроде ' or 1=1 -- остаётся просто текстом и не может изменить запрос — это защита от SQL-инъекций, как PreparedStatement в JDBC.

Частая ошибка

В JPQL используются имена сущностей и полей, а не таблиц и столбцов. Если класс Post отображён на таблицу posts, запрос select p from posts p падает:

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

То же бывает при опечатке в имени поля:

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']

А getSingleResult без подходящей строки бросает jakarta.persistence.NoResultException: No result found for query [...] — для этого и нужен getSingleResultOrNull.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.