Encodera

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

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

Оглавление

Нативный SQL

JPQL покрывает большинство задач, но иногда нужен сам SQL: функция, которая есть только в вашей базе, оконная функция, вручную настроенный запрос для отчёта. Нативные запросы позволяют отправить любой SQL через тот же EntityManager, с параметрами и в текущей транзакции. Цена — переносимость: запрос пишется под конкретную базу данных.

Загрузка сущностей

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

// src/main/java/com/example/blog/Main.java
List<Post> posts = em.createNativeQuery(
                "select * from Post where views > :min order by views desc", Post.class)
        .setParameter("min", 50)
        .getResultList();
posts.forEach(p -> IO.println(p.getTitle()));
Hibernate: select * from Post where views > ? order by views desc
Привет, Hibernate
Типы record в Java 25

В запросе настоящие имена таблиц и столбцов: Post, views, author_id. Если вторым аргументом передать класс-сущность, Hibernate превратит каждую строку в управляемый объект Post. Именованные параметры вида :min здесь тоже работают. Метод возвращает «сырой» List, поэтому компилятор предупреждает о непроверенном преобразовании.

Строки массивами

Если результат — не сущность, просите Object[]: по массиву на строку.

// src/main/java/com/example/blog/Main.java
List<Object[]> rows = em.createNativeQuery("""
                select a.name, count(p.id)
                from Author a left join Post p on p.author_id = a.id
                group by a.name
                order by a.name
                """, Object[].class)
        .getResultList();
rows.forEach(r -> IO.println(Arrays.toString(r)));
[Анна, 1]
[Мария, 1]
[Олег, 1]

Типы элементов зависят от JDBC-драйвера: здесь имя — String, а количество — Long.

Строки в запись

Hibernate умеет и вызывать конструктор записи, компоненты которой соответствуют столбцам:

// src/main/java/com/example/blog/AuthorPosts.java
package com.example.blog;
 
public record AuthorPosts(String name, long posts) {
}
// src/main/java/com/example/blog/Main.java
List<AuthorPosts> stats = em.createNativeQuery("""
                select a.name, count(p.id)
                from Author a left join Post p on p.author_id = a.id
                group by a.name
                order by a.name
                """, AuthorPosts.class)
        .getResultList();
stats.forEach(IO::println);
AuthorPosts[name=Анна, posts=1]
AuthorPosts[name=Мария, posts=1]
AuthorPosts[name=Олег, posts=1]

Столбцы сопоставляются по порядку: первый — с name, второй — с posts.

Что когда выбирать

ЗадачаИнструмент
Обычная выборка и фильтрацияJPQL
Фильтры, собираемые во время работыCriteria API
Возможности конкретной базы, сложные отчётыНативный SQL

Нативные запросы, как и массовые JPQL-изменения, идут в обход контекста персистентности: update через SQL не меняет уже загруженные сущности.

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

Если нативный запрос загружает сущности, он должен вернуть все столбцы, которые сущность отображает. Если выбрать только часть:

// src/main/java/com/example/blog/Main.java
em.createNativeQuery("select id, title from Post where views > :min order by views desc", Post.class)
Exception in thread "main" org.hibernate.exception.SQLGrammarException: Unable to find column position by name: author_id [Column "AUTHOR_ID" not found [42122-252]] [n/a]

Чтобы загрузить часть столбцов, используйте Object[] или запись вместо класса-сущности.

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

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