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 (набор данных)".