Contents | Prev | Next | Главная страница JDBCTM Guide: Getting Started


6 - PreparedStatement

6.1    Введение

Интерфейс PreparedStatement наследует от Statement и отличается от последнего следующим:

  1. Экземпляры PreparedStatement "помнят" скомпилированные SQL-выражения. Именно поэтому они называются "prepared" ("подготовленные").
  2. SQL-выражения в PreparedStatement могут иметь один или более входной (IN) параметр. Входной параметр - это параметр, чье значение не указывается при создании SQL-выражения. Вместо него в выражении на месте каждого входного параметра ставится знак ("?"). Значение каждого вопросительного знака устанавливается методами setXXX перед выполнением запроса.
Поскольку объекты PreparedStatement прекомпилированны, исполнение этих запросов может происходить несколько быстрее, чем в объектах Statement. В результате SQL-выражения, которые исполняются часто, в целях улучшения производительности создают в виде объектов PreparedStatement.

Оставаясь подклассом класса Statement, класс PreparedStatement наследует все функции от Statement. В дополнении к ним он добавляет методы установки входных параметров. Кроме того, три метода - execute, executeQuery и executeUpdate - модифицированы таким образом, что не имеют аргументов. Старые методы класса Statement (которые принимают SQL-выражения в качестве едиственного аргумента) не должны использоваться в объекте PreparedStatement.

6.1.1     Создание объектов PreparedStatement

Следующий фрагмент кода, где con - это объект Connection, создает объект PreparedStatement, содержащий SQL-выражение с двумя параметрами:

    PreparedStatement pstmt = con.prepareStatement(
        "UPDATE table4 SET m = ? WHERE x = ?");
Объект pstmt отныне содержит выражение "UPDATE table4 SET m = ? WHERE x = ?", которое уже отослано в СУБД и подготовлено для выполнения.

6.1.2     Передача входных (IN) параметров

Перед выполнением объекта PreparedStatement надо установить значения всех его параметров. Это делается с помощью методов setXXX, где XXX - это тип параметра. Например, если параметр имеет Java-тип long, используемый метод будет setLong. Первый аргумент методов setXXX - это порядковый номер параметра, а второй - значение, в которое надо его установить. Например, следующий код устанавливает первый параметр в значение 123456789, а второй - в 100000000:

    pstmt.setLong(1, 123456789);
    pstmt.setLong(2, 100000000);
После установки параметра его можно использовать при многократном выполнении выражения до тех пор, пока он не очистится методом clearParameters.

В режиме соединения по умолчанию (разрешена автофиксация) каждый запрос фиксируется или откатывается автоматически.

Один и тот же объект PreparedStatement может выполняться много раз, если нижестоящий драйвер или СУБД будут сохранять выражение (statement) в открытом состоянии даже после того как произойдет фиксация. Иначе не имеет смысла пытаться улучшить производительность заменой Statement на PreparedStatement.

Используя pstmt из предыдущего примера, следующий код устанавливает значения обоих параметров и выполняет pstmt 10 раз. Как уже было отмечено, БД не должна закрывать pstmt. В этомпримере первый параметр устанавливается в "Hi" и остается неизменным. Второй параметр устанавливается в последовательные целые значения, начиная от 0 и заканчивая 9.

    pstmt.setString(1, "Hi");  
    for (int i = 0; i < 10; i++) {
      pstmt.setInt(2, i);
      int rowCount = pstmt.executeUpdate();
    }

6.1.3     Совместимость типов данных входных параметров

XXX в начвании методов setXXX - это тип данных Java. Неявно он же является и типом данных JDBC (SQL), так как драйвер отображает тип данных Java на соответствующий JDBC-тип (согласно таблице из раздела 8.6.2) перед отсылкой JDBC-типа в БД. Следующий код устанавливает второй параметр объекта PreparedStatement pstmt в 44 с типом данных short:

    pstmt.setShort(2, 44);
Драйвер отошлет 44 в БД в виде типа JDBC SMALLINT, который является стандартным для Java-типа short.

На программисте лежит ответственность за совместимость Java-типов входных параметров и ожидаемых базой данных соответствующих JDBC-типов. Рассмотрим случай, когда ожидается SMALLINT. Если используется метод setByte, то драйвер отошлет значение TINYINT в БД. Это, вероятно, работать будет, так как многие БД преобразуют "похожие" типы данных друг в друга. Тем не менее, чтобы приложение могдо работать как можно с большим количеством СУБД, лучше использовать Java-типы, которые в точности соответствуют ожидаемым JDBC-типам. Если ожидается JDBC-тип данных SMALLINT, то использование именно setShort вместо setByte сделает приложение более переносимым.

6.1.4     Использование объектов

Программист может явно задать конвертирование входных параметров в определенный JDBC-тип с помощью метода setObject. Этот метод может принимать третий аргумент, указывающий целевой JDBC-тип данных. Перед отправкой в БД драйвер преобразует Object в указанный JDBC-тип.

Если JDBC-тип не задан, то драйвер просто преобразует Object в его ближайший JDBC-эквивалент в соответствии с таблицей из раздела 8.6.4, а затем пошлет его в БД. Это подобно использованию обычных методов setXXX; в обоих случаях драйвер отображает Java-типы в JDBC-типы данных перед отправкой значений в БД. Разница заключается в том, что если методы setXXX используют таблцу отображения Java-типов в JDBC-типы из раздела 8.6.2), то метод setObject использует отображение из таблицы в разделе 8.6.4.

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

6.1.5     Отсылка значений NULL в качестве входного параметра

Метод setNull позволяет отсылать значения NULL в БД как входные параметры. Хотя JDBC-тип параметра все же можно задать явно.

JDBC-значение NULL будет отосланов БД также в том случае, если методу setXXX будет передано Java-значение null (если метод принимает Java-объект в качестве аргумента). Тем не менее, метод setObject может принять значение null только в случае, если задан JDBC-тип.

6.1.6     Отправка очень больших входных параметров

Методы setBytes и setString могут отсылать неограниченное количество данных. Правда, иногда программисту легче передавать большие значения в виде маленьких кусков. Это делается установкой входного параметра в значение Java-потока ввода (input stream). Когда выполняется выражение, JDBC-драйвер будет производить последовательные вызовы из этого потока ввода, считывая его содержимое и пересылая его в виде значения параметра.

JDBC предоставляет три метода установки входных параметров в поток ввода: setBinaryStream для потоков, содержащих обычные байты, setAsciiStream для потоков ASCII-символов и setUnicodeStream для потоков Unicode-символов. Эти методы, в отличие от остальных методов setXXX, принимают дополнительный аргумент, равный количеству передаваемых в потоке байтов. Этот аргумент необходим, так как некоторые СУБД требуют указания размера данных перед их отсылкой.

Следующий код иллюстрирует отсылку файла в виде входного параметра запроса:

    java.io.File file = new java.io.File("/tmp/data");
    int fileLength = file.length();
    java.io.InputStream fin = new java.io.FileInputStream(file);
    java.sql.PreparedStatement pstmt = con.prepareStatement(
      "UPDATE Table5 SET stuff = ? WHERE index = 4");
    pstmt.setBinaryStream (1, fin, fileLength);
    pstmt.executeUpdate();
При выполнении запроса для доставки данных последовательно считывается поток ввода fin.

    


Содержание | Предыдущий | Следующий