CallableStatement
предоставляет унифицированный способ
вызова хранимых процедур в любой СУБД. Вызов процедуры осуществляется с помощью
escape-синтаксиса в одной из двух форм: с результирующим параметром и без него.
(См. раздел 4, "Запрос (Statement)"
об escape-синтаксисе). Результирующий параметр - это один из
типов выходных (OUT) параметров, являющийся возвращаемым значением хранимой процедуры.
Обе формы могут иметь переменное число аргументов на входе (параметры IN),
выходе (параметры OUT) или входных и выходных параметров одновременно (INOUT-параметры).
Вопросительный знак означает местоположение параметра.
Синтаксис вызова хранимой процедуры в JDBC показан ниже. Квадратные скобки означают, что то, что находится между ними, необязательно, и сами по себе не являются частью синтаксиса.
{call имя_процедуры[(?, ?, ...)]}Синтаксис для процедуры, возвращающей результат:
{? = call имя_процедуры[(?, ?, ...)]}Синтаксис хранимой процедуры без параметров:
{call имя_процедуры}Обычно тот, кто использует объект
CallableStatement
, уже знает,
что используемая СУБД поддерживает хранимые процедуры и какие именно процедуры имеются в БД. Тем не менее, чтобы это
выяснить, достаточно вызвать некоторые методы объекта DatabaseMetaData
.
Например, метод supportsStoredProcedures
возвращает true
,
если СУБД поддерживает хранимые процедуры, а метод
getProcedures
возвращает описания доступных процедур.
CallableStatement
наследует методы Statement
общей для обработки SQL-запросов, а также методы
PreparedStatement
для обработки входных (IN) параметров.
Все методы, объявленные в CallableStatement
обрабатывают
выходные (OUT) параметры следующим образом: указание
JDBC-типов данных (т.е. SQL-типов) выходных параметров, извлечение из них
значений или проверка их на NULL
.
CallableStatement
создаются методом prepareCall
объекта Connection
. Приведем пример, который создает экземпляр
CallableStatement
, содержащий вызов хранимой процедуры
getTestData
с двумя аргументами и без возвращаемого параметра:
CallableStatement cstmt = con.prepareCall( "{call getTestData(?, ?)}");Какими именно параметрами (IN, OUT или INOUT) являются знаки вопроса - зависит от самой хранимой процедуры
getTestData
.
CallableStatement
осуществляется с помощью методов
setXXX
, унаследованных от PreparedStatement
.
Типы передаваемых значений определяются тем, какой из методов
setXXX
используется (setFloat
для передачи
значений float
и т.п.).
JDBC-типы всех OUT-параметров хранимых процедур должны быть зарегистрирваны
перед их вызовом. Это необходимо, так как некоторым СУБД нужно передавать информацию
и типах данных. Регистрация типов данных выходного параметра
производится методом registerOutParameter
. Только в этом случае
после выполнения запроса методы getXXX
класса CallableStatement
смогут получить значения параметров. Необходимо использовать подходящий
по типу данных Java метод getXXX
в соответствии с
зарегистрированным JDBC-типом параметра. (Стандартное отображение типов
из JDBC в Java показано в таблице раздела
8.6.1.) Другими словами,
registerOutParameter
использует JDBC-тип
(тот, который подходит к JDBC-типу возвращаемого из БД значения), а
getXXX
преобразует его в тип Java.
Приведем пример регистрации выходных параметров, выполнения хранимой процедуры
cstmt
и считывание выходных параметров. Метод
getByte
извлекает байт из первого выходного параметра, а
getBigDecimal
возвращает объект BigDecimal
(с тремя цифрами после десятичной точки) из второго:
CallableStatement cstmt = con.prepareCall( "{call getTestData(?, ?)}"); cstmt.registerOutParameter(1, java.sql.Types.TINYINT); cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3); cstmt.executeQuery(); byte x = cstmt.getByte(1); java.math.BigDecimal n = cstmt.getBigDecimal(2, 3);В отличие от
ResultSet
, CallableStatement
не может
считывать большие значения последовательно (в потоке).
setXXX
(унаследованный от PreparedStatement
), так и метод
registerOutParameter
. Метод
setXXX
устанавливает входное значение параметра, а
registerOutParameter
регистрирует тип выходного значения.
Типы входного и выходного значений, зарегистрированных методом
registerOutParameter
, должны быть одинаковыми. Для чтения выходного значения
используется соответствующий метод getXXX
. Например, для параметра
типа byte нужно использовать метод установки значения
setByte
, передавать JDBC-тип данных
TINYINT
методу registerOutParameter
и использовать
getByte
для чтения выходного значения.
(В разделе 8 "Отображение типов JDBC на типы Java"
приведена более подробная информация о соответствии типов данных).
В следующем примере вызывается хранимая процедура
reviseTotal
с единственным INOUT-параметром. Метод
setByte
устанавливает значение параметра в 25
,
которое будет представлено базе данных как TINYINT
.
Далее метод registerOutParameter
регистрирует параметр как
TINYINT
. После выполнения хранимой процедуры возвращается
значение типа TINYINT
, которое будет считано методом
getByte
в виде типа byte
языка Java.
CallableStatement cstmt = con.prepareCall( "{call reviseTotal(?)}"); cstmt.setByte(1, 25); cstmt.registerOutParameter(1, java.sql.Types.TINYINT); cstmt.executeUpdate(); byte x = cstmt.getByte(1);
CallableStatement
, а затем выходные (OUT) параметры.
Если объект CallableStatement
возвращает несколько объектов
ResultSet
(с использованием метода execute
), то ВСЕ
результаты должны быть прочитаны перед первым обращением к выходным параметрам.
В этом случае для того, чтобы прочитать все результаты, надо последовательно
вызывать методы
Statement
getResultSet
,
getUpdateCount
и getMoreResults
до тех пор, пока не останется больше
результатов.
После этого значения выходных параметров могут быть извлечены спомощью методов
CallableStatement.getXXX
.
NULL
.
При этом методы getXXX возвращают
null
, 0
или false
, в зависимости от типа данных.
Как и в случае с ResultSet
, единственным способом узнать,
вернула ли процедура 0
, false
или NULL
,
является вызов метода wasNull
, который возвращает true
,
если последнее значение, считанное одним из методов
getXXX
был NULL
, и false
иначе. См. раздел
5, "ResultSet (набор данных)".