NativeQuery

Открыта

3.1 Знакомство

Еще одна полезная вещь, про которую хотелось бы рассказать – это NativeQuery. Как ты уже знаешь, с помощью NativeQuery можно писать запросы на родном SQL. Однако еще интереснее то, что ты можешь не использовать маппинг классов при получении результата запроса.

Я лучше покажу тебе пример:

List<Object[]> persons = session.createNativeQuery("SELECT * FROM Person").list();

В этом примере мы не передаем класс, который соответствует строкам результата запроса, вместо этого используется просто массив объектов Object.

Это может быть полезно, если ты хочешь сделать выборку всего пары колонок таблицы. Пример:

List<Object[]> persons = session.createNativeQuery("SELECT id, name FROM Person").list();

for(Object[] person : persons) {
    Number id = (Number) person[0];
    String name = (String) person[1];
}

Это чем-то похоже на подход JDBC, когда ты получаешь объект ResultSet и читаешь данные из его строк.

Однако Hibernate предлагает разные способы сделать это дело более надежным. Например, ты можешь указать тип колонок, которые хочешь вычитать. Пример:

Query<Object[]> query = session.createNativeQuery("SELECT id, name FROM Person");
query.addScalar("id", StandardBasicTypes.LONG);
query.addScalar("name", StandardBasicTypes.STRING);
List<Object[]> persons = query.list();

for(Object[] person : persons) {
    Long id = (Long) person[0];
    String name = (String) person[1];
}

3.2 Entity маппинг

Также ты можешь явно указать класс, который Hibernate должен использовать при парсинге результата NativeQuery. Сделать это можно разными способами.

Query<Person> query = session.createNativeQuery("SELECT * FROM Person")
    .addEntity(Person.class);
    .list();

И конечно, старый-добрый известный тебе формат:

Query<Person> query = session.createNativeQuery("SELECT * FROM Person", Person.class).list();

Первый подход – это родной подход Hibernate, а второй – это подход JPA. Подход JPA удобнее и лаконичнее, так как этот стандарт был придуман уже после того, как Hibernate просуществовал много лет. А Hibernate развивался и был вынужден поддерживать старые подходы для сохранения совместимости со своими старыми версиями.

Кстати, благодаря своему подходу Hibernate позволяет подключать не один класс к маппингу результата запроса, а несколько классов. Пример:

List<Phone> results = session.createNativeQuery(
    "SELECT {ph.*}, {pr.*}" +
    "FROM Phone ph" +
    "JOIN Person pr ON ph.person_id = pr.id")
.addEntity("ph", Phone.class)
.addJoin("pr", "ph.person")
.list();

for (Phone. phone : results) {
           	assertNotNull( phone.getPerson().getName() );
}

Такой подход с использованием NativeQuery можно использовать для ускорения выборки данных из базы. Если ты знаешь, что какие-то колонки тебе не нужны, ты можешь их не указывать в запросе.

Также ты можешь сразу загрузить все дочерние сущности, даже если Hibernate хочет использовать Cache или механизм LazyLoading. Кроме того, у твоих дочерних сущностей может быть много колонок в базе, и ты можешь отобрать только некоторые из них.

3.3 DTO-маппинг

Также Hibernate позволяет использовать для маппинга результата не Entity-классов. Классы, у которых нет никаких аннотаций, и которые не замаплены на любые таблицы.

Пример:

public class PersonSummaryDTO {
    private Number id;
    private String name;

    public Number getId() {
    	return id;
    }

    public void setId(Number id) {
    	this.id = id;
    }

    public String getName() {
    	return name;
    }

    public void setName(String name) {
    	this.name = name;
	}
}

List<PersonSummaryDTO> dtos = session.createNativeQuery(
    "SELECT p.id as \"id\", p.name as \"name\" FROM Person p")
.setResultTransformer(Transformers.aliasToBean(PersonSummaryDTO.class) )
.list();

Так как аннотаций у класса PersonSummaryDTO нет, то имена колонок в SQL-запросе должны точно совпадать с именами полей класса PersonSummaryDTO.

Такое бывает очень полезно, если ты читаешь данные из внешней базы данных, к которой твое приложение подключено только в режиме read-only. То есть тебе дали доступ к таблицам у которых по 50+ колонок, они хранят данные в денормализованном виде для ускорение выборки.

Или же допустим, что кто-то решил хранить иерархию классов в одной таблице, а за пять лет эта таблица так разрослась, что черт ногу сломит. Тебе нужно выбрать из этой таблицы пару колонок (Id и имя пользователя) и отдать их клиенту.

Думаю, ты понял, но если хочешь погрузиться в эту тему глубже, то подробнее можно почитать по ссылке:

Native SQL Queries

1
Задача
Модуль 4. Работа с БД,  16 уровень2 лекция
Недоступна
Метод list
Если ранее не подключал зависимости, то подключи их. Для этого используй Alt + Ctrl + Shift + S (в Идее), вкладка Libraries. Зависимости можно скачать здесь: https://javarush.com/downloads/ide/javarush/hibernate.zip

Еще немного про DTO-маппинг в видео

Комментарии (6)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Надежда
Уровень 104
Expert
15 октября 2023, 14:13
Если у query вызывается метод list(), то соответственно возвращаемый тип выражения Query<Person> query = session.createNativeQuery("SELECT * FROM Person", Person.class).list(); не Query<Person>, а List<Person>. Поэтому надо: List<Person> persons = session.createNativeQuery("SELECT * FROM persons", Person.class).list();
Надежда
Уровень 104
Expert
15 октября 2023, 14:09
Ошибка на ошибке. Query<Person> query = session.createNativeQuery("SELECT * FROM Person") .addEntity(Person.class); .list(); Во-первых, метод createNativeQuery возвращает NativeQuery<Person>, во-вторых, сам запрос неверный: раз у нас нативный SQL-запрос, то надо обращаться не к entity-классу Person, а к таблице persons,' в-третьих, что это за .list(); ? Метод list() вызываем на объекте query, поэтому надо query.list(); Исправленный код: NativeQuery<Person> nativeQuery = session.createNativeQuery("SELECT * FROM person") .addEntity(Person.class); query.list();
Надежда
Уровень 104
Expert
15 октября 2023, 12:53
В запросах SQL обычно работают с именами таблиц, а не с классами сущностей. И если вы используете нативные SQL-запросы, то и обращайтесь не к entity-классу, а к таблице. Поэтому вместо List<Object[]> persons = session.createNativeQuery("SELECT id, name FROM Person").list(); надо List<Object[]> persons = session.createNativeQuery("SELECT id, name FROM persons").list();
Эльдар
Уровень 108
Expert
11 октября 2023, 08:43
Отдельное спасибо за видео=)) Очень понятно объясняет.
Стрелков Игорь
Уровень 36
20 августа 2023, 14:46


😨
Дмитро Гашук
Уровень 111
Expert
9 февраля 2023, 14:39
Что это значит, я что-то пропустил, где мы можем фигурные скобки использовать ? List<Phone> results = session.createNativeQuery( "SELECT {ph.*}, {pr.*}" + "FROM Phone ph" + "JOIN Person pr ON ph.person_id = pr.id")