Виды statements

Самый простой интерфейс Statement мы уже видели. И хотя он вполне пригоден для работы, для сложных запросов он подходит не так хорошо. В некоторых источниках высказывается мнение, что использовать Statement вообще не надо — вместо него подходят более сложные и более функционально насыщенные интерфейсы.

  • PreparedStatement
  • CallableStatement

Возникает вполне резонный вопрос — а зачем эти интерфейсы нужны? Давай разбираться.

Для начала мы рассмотрим интерфейс PreparedStatement и другие возможности JDBC. К интерфейсу CallableStatement обратимся позже — его использование, во-первых, не так часто встречается, а во-вторых — после всего рассмотренного про него разговор уже можно делать достаточно коротким.

Кроме того, PreparedStatement отлично помогает от популярного подхода к взлому базы данных, который называется SQL Injection.

Но подробнее об этом немного позднее.

PreparedStatement

Если попытаться перевести название PreparedStatement, то можно получить что-то вроде “подготовленный оператор”. Самым важным здесь является слово “подготовленный”. В чем заключается “подготовленность”?

Прежде чем мы рассмотрим этот вопрос, предлагаю увидеть достаточно важный с точки зрения удобства момент, который возникает очень часто. Итак, в каком-либо приложении нам надо вставить данные о контакте в таблицу CONTACT. Для этого нам надо подготовить запрос наподобие такого:

INSERT INTO JC_CONTACT (FIRST_NAME, LAST_NAME, PHONE, EMAIL) VALUES (‘Harry’,'Potter','+79112345678','harry@example.com);

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

Если мы это делаем в отдельной функции, то получается что-то такое:


public String buildInsert(String firstName,, String lastName, String phone, String email) {
    String sql = "INSERT INTO JC_CONTACT (FIRST_NAME, LAST_NAME, PHONE, EMAIL) “+
             	”VALUES ('" + firstName + "','" + lastName + "','" + phone + "','" + email + ")";
    return sql;
}

Мы передаем в функцию параметров имя, фамилию, телефон и адрес в виде, и из них составляем строку SQL-запроса. Кавычки немного портят картину, но пока не страшно.

Ок, а что делать с числами? Их не надо окружать кавычками. Опаньки, в одном случае надо кавычки, в другом — не надо. Ситуация усложняется.

Теперь добавим еще одну проблему — а если внутри строки есть ординарная кавычка (и даже не одна)? Надо предварительно искать такие кавычки и обрабатывать их. Мдааа. Как-то неуютно начинаем себя ощущать.

Если теперь прибавить обработку дат, то задача становится совсем скучной — надо делать огромное количество работы. С датами вообще неприятно — разные SQL-сервера принимают для дат разные форматы.

Итак, что мы видим? Если нам надо использовать параметры внутри запроса, то в ручном режиме построение запроса становится очень неприятным делом. Причем не просто неприятным — я бы даже сказал занудным. Надо учитывать огромное количество случаев, и это ужасно скучная работа. В основном именно для таких случаев и был предложен интерфейс PreparedStatement.

Этот запрос позволяет вам сделать две вещи:

  • Заранее подготовить запрос с указанием мест, где будут подставляться параметры
  • Установить параметры определенного типа и выполнить после этого запрос с уже установленными параметрами

Пример использования PreparedStatement

Конструкция для PreparedStatement для нашего варианта установки параметров будет выглядеть вот так:


// Переменные для примера
String firstName = "Harry";
String lastName = "Potter";
String phone = "+12871112233";
String email = "harry@example.com";
 
// Запрос с указанием мест для параметров в виде знака "?"
String sql = "INSERT INTO JC_CONTACT (FIRST_NAME, LAST_NAME, PHONE, EMAIL) VALUES (?, ?, ?, ?)";
 
// Создание запроса. Переменная con — это объект типа Connection
PreparedStatement stmt = con.prepareStatement(sql);
 
// Установка параметров
stmt.setString(1, firstName);
stmt.setString(2, lastName);
stmt.setString(3, phone);
stmt.setString(4, email);
 
// Выполнение запроса
stmt.executeUpdate();

Как видишь, все достаточно несложно.

Во-первых, при написании SQL-запроса на места, куда надо будет подставлять параметры, записываются знаки вопроса — “?”.

Во-вторых — запрос создается через вызов con.prepareStatement().

В-третьих — установка параметров идет через указание номера и значения. Обрати внимание, что номер параметров начинаются с 1, а не с 0, как мы привыкли при работе с массивами и коллекциями.

Интерфейс PreparedStatement содержит методы для установки строк — setString(), для установки чисел — setInt(), setLong(), setDouble(), для установки дат — setDate(). И более сложных типов — это можно увидеть в документации.

В-четвертых — вызов stmt.executeUpdate() выполняется уже без указания строки запроса.

Крайне настоятельно рекомендую подружиться с PreparedStatement — это очень эффективный инструмент.