Contents | Prev | Next | JDBCTM Guide: Getting Started |
PreparedStatement
наследует от Statement
и отличается
от последнего следующим:
PreparedStatement
"помнят" скомпилированные
SQL-выражения. Именно поэтому они называются "prepared" ("подготовленные").
PreparedStatement
могут иметь один или более
входной (IN) параметр. Входной параметр - это параметр, чье значение не
указывается при создании SQL-выражения. Вместо него в выражении на месте
каждого входного параметра ставится знак ("?"). Значение каждого
вопросительного знака устанавливается методами setXXX
перед
выполнением запроса. PreparedStatement
прекомпилированны, исполнение этих запросов может
происходить несколько быстрее, чем в объектах Statement
. В
результате SQL-выражения, которые исполняются часто, в целях улучшения
производительности создают в виде объектов PreparedStatement
.
Оставаясь подклассом класса Statement
, класс
PreparedStatement
наследует все функции от Statement
.
В дополнении к ним он добавляет методы установки входных параметров. Кроме того,
три метода - execute
, executeQuery
и
executeUpdate
- модифицированы таким образом, что не имеют
аргументов. Старые методы класса Statement
(которые принимают
SQL-выражения в качестве едиственного аргумента) не должны использоваться в
объекте PreparedStatement
.
con
- это объект
Connection
, создает объект PreparedStatement
,
содержащий SQL-выражение с двумя параметрами:
PreparedStatement pstmt = con.prepareStatement( "UPDATE table4 SET m = ? WHERE x = ?");Объект
pstmt
отныне содержит выражение
"UPDATE table4 SET m = ? WHERE x = ?"
, которое уже отослано в СУБД
и подготовлено для выполнения.
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(); }
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
сделает приложение более
переносимым.
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
.
setNull
позволяет отсылать
значения NULL
в БД как входные параметры. Хотя JDBC-тип параметра
все же можно задать явно.
JDBC-значение NULL
будет отосланов БД также в
том случае, если методу setXXX
будет передано Java-значение
null
(если метод принимает Java-объект в качестве аргумента). Тем
не менее, метод setObject
может принять значение null
только в случае, если задан JDBC-тип.
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
.