JavaRush /Курсы /Модуль 4. Работа с БД /Запросы с параметрами

Запросы с параметрами

Модуль 4. Работа с БД
10 уровень , 3 лекция
Открыта

4.1 Параметры к запросам

Hibernate позволяет передавать параметры запросам. Таким образом сильно упрощается вся работа с запросами и базой данных.

Очень редко можно встретить неизменяемые запросы. Вначале ведь кажется, что тебе нужно вернуть из базы просто список товаров. А потом выясняется, что нужен актуальный список товаров для определенного пользователя на определенную дату. Отсортированный по нужному полю, и еще не весь список, а определенная страница: например, товары с 21 по 30-й.

И именно эту задачу решают параметризированные запросы. Ты пишешь на HQL запрос, а потом значения, которые можно поменять заменяете на “специальные имена” – параметры. И потом отдельно при выполнении запроса можно передать значения этих параметров.

Давай напишем HQL-запрос, который будет возвращать все задачи для пользователя с определенным именем:


from EmployeeTask where employee.name = "Иван Иванович"

А теперь заменим имя на параметр:


from EmployeeTask where employee.name = :username

И вот как будет выглядеть наш Java-код для поиска задач:


String hql = "from EmployeeTask where employee.name = :username";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameter("username", "Иван Иванович");
List<EmployeeTask> resultLIst = query.list();

Также вместо имени параметр, можно использовать просто номер:


String hql = "from EmployeeTask where employee.name = :1";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameter(1, "Иван Иванович");
List<EmployeeTask> resultLIst = query.list();

Хотя лучше, конечно, использовать имя – читать и поддерживать такой код гораздо легче.

4.2 Метод setParameterList().

Также бывают случаи, когда значение параметра не одно, а представляет список объектов. Например, мы хотим проверить, что профессии сотрудников содержатся в определенном списке.

Как бы это можно было сделать:


String hql = "from EmployeeTask where occupation IN (:occupation_list)";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameterList("occupation_list", new String[] {"Программист", "Тестировщик"});
List<EmployeeTask> resultLIst = query.list();

В качестве значения параметра можно передать 4 вида списка:

  • массив объектов: Object[]
  • коллекция: Collection
  • типизированный массив: T[]
  • типизированная коллекция: Collection<T>

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


String hql = "from EmployeeTask where occupation IN (:occupation_list)";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameterList("occupation_list", new String[] {"Программист", "Тестировщик"}, String.class);
List<EmployeeTask> resultLIst = query.list();

При работе с параметрами-списками также можно использовать номер вместо имени параметра. Но опять-таки с именем удобнее.

4.3 Защита от SQL Injection

Одно из важнейший назначений параметров – это защита базы от SQL-инъекций. Многие программисты-новички вместо использования параметров просто бы склеили строку из нескольких частей.

Вместо того, чтобы написать так:


String hql = "from EmployeeTask where employee.name = :username";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameter("username", "Иван Иванович");
List<EmployeeTask> resultLIst = query.list();

Написали бы так:


String hql = "from EmployeeTask where employee.name = " + "Иван Иванович";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
List<EmployeeTask> resultLIst = query.list();

Никогда так не делайте! Никогда не склеивай SQL/HQL-запрос из нескольких частей. Потому что рано или поздно имя пользователя придет тебе с клиента. И злобный хакер в качестве имени клиента передаст вам строку типа ""Иван"; DROP TABLE user;"

И тогда твой запрос к базе данных примет вид:


from EmployeeTask where employee.name = "Иван"; DROP TABLE user;

И это еще хорошо, если твои данные просто удалят. Можно ведь написать и так:


from EmployeeTask where employee.name = "Иван";
UPDATE user SET password = '1' WHERE user.role = 'admin'

Или так:


from EmployeeTask where employee.name = "Иван";
UPDATE user SET role = 'admin' WHERE user.id = 123;
1
Задача
Модуль 4. Работа с БД, 10 уровень, 3 лекция
Недоступна
Параметры к запросам
Параметры к запросам
1
Задача
Модуль 4. Работа с БД, 10 уровень, 3 лекция
Недоступна
Метод setParameterList
Метод setParameterList
Комментарии (12)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Alexander Уровень 81
25 февраля 2026
В первой задаче название метода смутило, пытался запрос через between делать
Artur Chakov Уровень 90
16 января 2026
Опять же не понятно, почему во второй задаче не принимает ответ с явно указанным типом коллекции: query.setParameterList("in", in, String.class);
Иван Корниенко Уровень 109
1 августа 2024
Также вместо имени параметр, можно использовать просто номер:
String hql = "from EmployeeTask where employee.name = :1";
У меня сработало только если заменить
:1
на

?1
И. Ж. Уровень 41
15 июля 2024
Почему то не хочет такое принимать, пишет синтаксическая ошибка: String query = "TRUNCATE TABLE :tablename"; rowsCount = session.createNativeQuery(query).setParameter("tablename", nameTable).executeUpdate(); Пришлось вернуть: rowsCount = session.createNativeQuery("TRUNCATE TABLE " + nameTable).executeUpdate(); И такое не нравиться, тоже пришлось вернуть с иньекцией: String query = "SELECT COUNT(*) FROM :tablename"; NativeQuery<Integer> nativeQuery = session.createNativeQuery(query, Integer.class); nativeQuery.setParameter("tablename", nameTable); countRows = nativeQuery.uniqueResult();
Siarhei Уровень 46
31 марта 2023
Объясните, кто знающий-понимающий... А почему так не дропнется таблица?:

String hql = "from EmployeeTask where employee.name = :username";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameter("username", "\"Иван\"; DROP TABLE user;");
List<EmployeeTask> resultLIst = query.list();
Станислав Future Уровень 39
25 апреля 2023
потому что в этом месте ожидается только один параметр. все запросы, если такие имеются, идущие после одного параметра просто отбрасываются
Michael Уровень 41
8 мая 2023
В параметр передается строка. И если в строке есть кавычки, то они будут экранированы символом "\". Т.е. не получится неожиданно завершить текстовый параметр и начать новую команду sql. "\" тоже будет экранирован. Т.е. "\"Иван\"; DROP TABLE user;" -> "\\\"Иван..."
Anonymous #3272489 Уровень 92 Expert
28 сентября 2023
чат gpt дал (как по мне) более понятное объяснение, делюсь 'setParameter' предназначен для установки значения параметра :username в вашем HQL-запросе. Всё, что вы передаете в этот метод, будет рассматриваться как значение параметра, а не как часть SQL-запроса. Даже если вы попытаетесь вставить вредные символы (например, ; или SQL-инструкции), Hibernate преобразует их таким образом, что они не могут быть интерпретированы как SQL-команды, предотвращая тем самым SQL-инъекции.
Fargys1879 Уровень 1
26 декабря 2023
Есть вроде как вспомогательный Класс который называется QueryParameters.class

public void validateParameters() throws QueryException {
        int types = this.positionalParameterTypes == null ? 0 : this.positionalParameterTypes.length;
        int values = this.positionalParameterValues == null ? 0 : this.positionalParameterValues.length;
        if (types != values) {
            throw new QueryException("Number of positional parameter types:" + types + " does not match number of positional parameters: " + values);
        }
    }
Но возможно он не в полной мере защищает от инъекций.Полная защита уже идет на уровне JDBC при приминении PrepareStatement, где уже участвуют анализаторы запроса и на фазе компиляции уже не допустят использования такого параметра
BlackGrizzli Team Уровень 46
26 февраля 2023
Не совсем понятно, то ли нам говориться всегда писать HQL запросы через параметры, так как это безопасно, то ли в каких то особых случаях ? Правильно ли я понял, если хотим просто вытащить данные таблицы, без каких либо критериев, которые прописываем например в where, ТО это вообще неизменяем запрос, а если запрос содержит where user = "admin" например, то это уже считается лазейкой для взлома и нужно "admin" задавать через setParameter() ?
Станислав Future Уровень 39
25 апреля 2023
если в запросе нет и в будущем не предвидится параметр, который придет с клиента - то это безопасный запрос. все остальное только с установкой параметров
Андрей Федоров Уровень 108 Expert
25 января 2023
эх бобби какой же ты хулиган)))