В этой главе обсуждается работа с реляционными базами данных DB2, Informix, ODBC, Oracle и Sybase. Рассматривается, как запрашивать информацию из БД и использовать её в приложении, как работать с транзакциями и как выполнять хранимые процедуры.
Не забывайте, что, если Ваше приложение работает на Netscape FastTrack Server, а не на Netscape Enterprise Server, оно может иметь доступ только к серверам БД, использующим стандарт ODBC.
В главе имеются следующие разделы:
Сервис LiveWire Database Service даёт возможность взаимодействовать с
реляционной БД различными способами.
Вы можете:
Об установке и обслуживании соединений БД см. Главу 8, "Соединение с Базой Данных".
Наиболее простым и быстрым способом отобразить результаты выполнения запросов к
БД является использование метода
SQLTable
объекта database
или объекта Connection
.
Метод SQLTable
принимает оператор SQL SELECT
и
возвращает HTML-таблицу.
Каждый ряд и столбец в запросе это ряд и столбец таблицы. Таблица HTML имеет
также заголовочную ячейку для каждого столбца таблицы БД.
Метод SQLTable
не даёт Вам возможности управлять форматированием
вывода. Кроме того,
если вывод содержит объект Blob
, этот объект не выводится как
изображение.
(Об использовании blob см. раздел "Работа с Бинарными
Данными"). если Вы хотите специализировать вывод, используйте курсор БД для
создания Вашей собственной функции вывода/отображения. См. также "Манипуляции
с Результатами Выполнения Запросов с Помощью Курсоров".
В качестве примера: если myconn
это Connection
-объект,
следующий оператор JavaScript отображает результат выполнения запроса к БД в
виде таблицы:
myconn.SQLTable("select * from videos");
Вот первая часть таблицы, которая могла бы быть сгенерирована этим оператором:
Метод execute
объекта database
или объекта Connection
даёт приложению возможность выполнять произвольный оператор SQL. Использование execute
называется выполнением передаваемого SQL, поскольку этот метод передаёт SQL
непосредственно на сервер.
Вы можете использовать execute
для выполнения оператора SQL любого
языка определения данных/data definition language (DDL) или языка
манипулирования данными/data manipulation language
(DML), поддерживаемого сервером БД. Примером могут служить операторы CREATE
,
ALTER
и DROP
. Хотя Вы можете использовать execute
для выполнения любого оператора SQL, Вы не можете с помощью этого метода
возвращать данные.
Обратите внимание, что execute
служит для выполнения стандартных
операторов SQL, а не для выполнения расширений SQL, поставляемых некоторыми
производителями БД. Например, Вы не можете вызвать функцию Oracle describe
или функцию Informix load
из метода execute
.
Для выполнения передаваемых операторов SQL просто задайте оператор SQL как
параметр метода
execute
. Например, Вам нужно удалить таблицу из БД, на которую
имеется ссылка в свойстве oldtable
объекта project
.
Чтобы выполнить это, Вы можете использовать такой вызов метода:
connobj.execute("DROP TABLE " + project.oldtable);
При использовании execute
Ваш SQL-оператор
обязан строго соответствовать требованиям синтаксиса SQL данного сервера БД.
Например, некоторые серверы требуют, чтобы каждый оператор SQL заканчивался
символом "точка с запятой". Дополнительно см. документацию к Вашему серверу БД.
Если Вы не стартуете транзакцию явно, единственный оператор подтверждается
автоматически.
Об управлении транзакциями см. "Обслуживание Транзакций".
Для выполнения некоторых операций, таких как создание и удаление таблиц, Вам могут понадобиться права доступа, предоставляемые администратором базы данных. См. документацию Вашего сервера БД и обратитесь к администратору сервера БД.
Часто Вам необходимо не просто отобразить таблицу с результатами выполнения
запроса, но изменить форматирование этих результатов или даже выполнить
какую-нибудь их обработку.
Для манипуляций результатами выполнения запроса Вы работаете с курсором БД,
возвращаемым запросом к БД. Для создания экземпляра класса Cursor
вызовите метод cursor
объекта database
или объекта
Connection
, передав ему оператор SQL SELECT
в качестве
параметра.
Вы можете представить курсор как виртуальную таблицу с рядами и столбцами, специфицированный по запросу. Курсор предполагает также понятие текущего ряда, который в реальности является указателем на ряд виртуальной таблицы. Когда Вы выполняете операции с курсором, они обычно воздействуют на текущий ряд.
По окончании работы, закройте курсор БД путём вызова его метода close
.
Соединение с БД не может быть освобождено, пока не закрыты все ассоциированные с
ним курсоры. Например, если Вы вызываете метод release
объекта
Connection
и это соединение имеет ассоциированный курсор, который
не был закрыт, соединение не будет освобождено, пока курсор не будет закрыт.
В таблице обобщены методы и свойства класса Cursor
.
Cursor
Метод или Свойство | Описание |
---|---|
Свойства, соответствующие каждому столбцу курсора. Имя каждого свойства
| |
Полную информацию об этих методах см. в описании класса Cursor
в
книге
"Серверный JavaScript.
Справочник"
.
Как только приложение установило соединение с БД, Вы можете создать курсор путём
вызова метода cursor
ассоциированного объекта database
или Connection
. Создание объекта
Cursor
также открывает курсор в БД. Вам не нужно выполнять
отдельную команду open.
Можно предоставить следующую информацию при создании объекта Cursor
:
SELECT
, поддерживаемый сервером БД. Чтобы
гарантировать независимость от вида БД,
используйте синтаксис SQL 89/92. Курсор создаётся как виртуальная таблица
результата выполнения этого оператора SQL.SELECT
будет таким:
"select count(*) from videos"
, Вы не сможете создать обновляемый
курсор.
Например, следующий оператор создаёт курсор для записей таблицы CUSTOMER
.
Записи содержат столбцы id
, name
и city
и
упорядочены по значениям столбца id
.
custs = connobj.cursor ("select id, name, city
from customer
order by id");
Этот оператор устанавливает в переменную custs
объект Cursor
.
Запрос SQL может вернуть следующие ряды:
1 Sally Smith Suva
2 Jane Doe Cupertino
3 John Brown Harper's Ferry
Затем Вы можете получить доступ к этой информации через использование методов Cursor
-объекта custs
.
Этот объект имеет свойства id
, name
и city
,
соответствующие столбцам виртуальной таблицы.
Когда Вы первоначально создаёте Cursor
-объект, указатель
позиционируется сразу перед первым рядом виртуальной таблицы. В последующих
разделах рассматривается, как получить информацию из виртуальной таблицы.
Вы можете также использовать оператор конкатенации строк (+) и строковые
переменные (такие как значения свойств client
или
request
) при конструировании оператора SELECT
.
Например, следующий вызов использует ранее сохранённый customer ID для
последующей специализации запроса:
custs = connobj.cursor ("select * from customer where id = "
+ client.customerID);
При попытке создания Cursor
-объекта Вы можете столкнуться с
различными проблемами. Например, если оператор SELECT
в вызове
метода cursor
обращается к несуществующей таблице, БД возвращает
ошибку, и метод cursor
возвращает null вместо
Cursor
-объекта. В этой ситуации Вы должны использовать методы majorErrorCode
и
majorErrorMessage
для определения возникшей ошибки.
В качестве второго примера, предположим, что оператор SELECT
обращается к существующей таблице, в которой нет рядов. В этом случае БД может
не возвратить ошибку, а метод cursor
возвратит верный Cursor
-объект. Однако, поскольку этот объект не содержит рядов, при первой попытке
использования метода next
в этом объекте он возвратит false
.
Ваше приложение должно проверять возможность возникновения такой ситуации.
Когда Вы создаёте курсор, он получает свойство
colName
для каждого именованного столбца виртуальной таблицы (отличное от свойств,
соответствующих агрегатным функциям), как определено оператором SELECT
.
Вы можете получать доступ к значениям текущего ряда, используя эти свойства. В
вышеприведённом примере курсор имеет свойства для столбцов id
,
name
и city
. Вы можете вывести значения для первого
возвращённого ряда, используя следующие операторы:
// Создаётся Cursor-объект.
custs = connobj.cursor ("select id, name,
city
from customer order by id");
// Прежде чем продолжить, убедитесь, что курсор действительно был возвращён
// и не было ошибки БД.
if ( custs && (connobj.majorErrorCode() == 0) ) {
// Получаем первый ряд.
custs.next();
// Отображаем значение.
write ("<B>Customer Name:</B>" + custs.name + "<BR>");
write ("<B>City:</B> " + custs.city + "<BR>");
write ("<B>Customer ID:</B> " + custs.id);
//Закрываем курсор.
custs.close();
}
Сначала текущим рядом является первый ряд таблицы.
Выполнение метода next
передвигает текущий ряд на первый ряд
таблицы.
Например, предположим, что это первый ряд курсора:
1 Sally Smith Suva
Customer Name: Sally Smith
City: Suva
Customer ID: 1
Вы можете обращаться к свойствам Cursor
-объекта (или, в
действительности, объекта JavaScript) как к элементам массива. Элемент массива с
индексом [0] соответствует первому столбцу, элемент
массива с индексом [1]
соответствует второму столбцу, и так далее.
Например, Вы можете использовать индекс для отображения тех же значений столбцов, что и в предыдущем примере:
write ("<B>Customer Name:</B> " + custs[1] + "<BR>");
write ("<B>City:</B> " + custs[2] + "<BR>");
write ("<B>Customer ID:</B>
" + custs[0]);
Эта техника особенно применима внутри циклов. Например,
Вы можете создать Cursor
-объект с названием custs
и
вывести результат выполнения запроса в виде таблицы
HTML с помощью следующего кода:
// Создаётся Cursor-объект.
custs = connobj.cursor ("select id, name, city
from customer order by id");
// Прежде чем продолжить, убедитесь, что курсор действительно был возвращён
// и не было ошибки БД.
if ( custs && (connobj.majorErrorCode() == 0) ) {
write ("<TABLE BORDER=1>");
// Отображаем имена столбцов как заголовки.
write("<TR>");
i = 0;
while ( i < custs.columns() ) {
write("<TH>", custs.columnName(i), "</TH>");
i++;
}
write("</TR>");
// Отображаем каждый ряд виртуальной таблицы.
while(custs.next()) {
write("<TR>");
i = 0;
while ( i < custs.columns() ) {
write("<TD>", custs[i], "</TD>");
i++;
}
write("</TR>");
}
write ("</TABLE>");
// Закрываем курсор.
custs.close();
}
Этот код может вывести примерно такую таблицу:
ID | NAME | CITY |
---|---|---|
В примере использованы методы, обсуждаемые в последующих разделах.
Операторы SELECT
могут запрашивать значения,
не являющиеся столбцами в БД, такие как агрегатные значения и выражения SQL. Для
таких значений Cursor
-объект не имеет именованного свойства. Вы
можете получить к ним доступ только через использование свойства индекса массива Cursor
-объекта для данного значения.
В следующем примере создаётся курсор с именем empData
, делается
переход к первому ряду этого курсора и затем отображается значение, запрошенное
агрегатной функцией MAX
. Выполняется также проверка того, что
результаты из БД верны, перед тем как использовать их:
empData = connobj.cursor ("select min(salary), avg(salary),
max(salary) from employees");
if ( empData && (connobj.majorErrorCode() == 0) )
{
rowexists = empData.next();
if (rowexists)
{ write("Highest salary is ", empData[2]); }
}
Во втором примере создаётся курсор с именем empRows
для подсчёта
количества рядов в таблице,
выполняется переход к ряду в этом курсоре, а затем отображается количество рядов
параллельно с выполнением проверки на верность данных:
empRows = connobj.cursor ("select count(*) from employees");
if ( empRows && (connobj.majorErrorCode() == 0) ) {
rowexists = empRows.next();
if (rowexists) { write ("Number of rows in table: ", empRows[0]); }
}
Сначала указатель курсора позиционируется перед первым рядом виртуальной таблицы.
Метод
next
используется для перемещения между записями виртуальной
таблицы. Этот метод передвигает указатель на следующий ряд и возвращает true
,
когда следующий ряд виртуальной таблицы найден. Если следующего ряда нет, next
возвращает false
.
Например, предположим, что виртуальная таблица имеет
столбцы с названиями title
,
rentalDate
и dueDate
. Следующий код использует next
для итерации по рядам и отображения значений столбцов в таблице:
// Создаём курсор.
custs = connobj.cursor ("select * from customer");
// Проверяем курсор и отсутствие ошибок БД.
if ( custs && (connobj.majorErrorCode() == 0) ) {
write ("<TABLE>");
// Итерация по рядам с отображением значений.
while (custs.next()) {
write ("<TR><TD>" + custs.title + "</TD>" +
"<TD>" + custs.rentalDate + "</TD>" +
"<TD>" + custs.dueDate + "</TD></TR>");
}
write ("</TABLE>");
// Всегда закрывайте курсоры по окончании работы!
custs.close();
}
Вы не всегда можете находиться в нужном месте в курсоре. Например, предположим, что Вы создаёте курсор и, пока Вы работаете с ним, кто-то добавляет ряд в таблицу. В зависимости от установок БД, этот ряд может появиться в Вашем курсоре. Исходя из этого, когда это удобно (как при обновлении рядов), Вы можете сделать так, чтобы Ваш код проверял, находится ли указатель в нужном ряду.
Метод columns
класса Cursor
возвращает количество столбцов в курсоре. Этот метод не принимает параметров:
custs.columns()
Вы можете использовать этот метод, если нужно итерировать по каждому столбцу курсора.
Метод columnName
класса Cursor
возвращает имя столбца
виртуальной таблицы. Этот метод принимает параметр - целое число,
специфицирующее порядковый номер столбца, начиная с 0. Первым столбцом
виртуальной таблицы является столбец 0, вторым - столбец 1, и так далее.
Например, следующее выражение присваивает имя первого столбца курсора custs
переменной header
:
header = custs.columnName(0)
Если Ваш оператор SELECT
использует шаблон (*) для выбора
всех столбцов таблицы,
метод columnName
не гарантирует, что порядок присвоения номеров
столбцам будет тем же. То есть, предположим, у Вас есть оператор:
custs = connobj.cursor ("select * from customer");
Если таблица customer имеет 3 столбца, ID, NAME и CITY, Вы не сможете заранее
предугадать, который из этих столбцов будет custs.columnName(0)
. (Конечно,
есть гарантия, что последовательные вызовы columnName
дадут
аналогичный результат). Если порядок для Вас важен, можно жёстко кодировать
имена в операторе выборки, как здесь:
custs = connobj.cursor ("select ID, NAME, CITY from customer");
В этом операторе, custs.columnName(0)
это ID, custs.columnName(1)
это NAME, а custs.columnName(2)
это CITY.
Вы можете использовать обновляемый курсор для модифицирования таблицы на основе
текущего ряда курсора. Чтобы запросить обновляемый курсор, добавьте
дополнительный параметр true
при создании курсора, как в этом
примере:
custs = connobj.cursor ("select id, name, city from customer", true)
Чтобы курсор был обновляемым, оператор SELECT
обязан быть
обновляемым запросом
(запросом, позволяющим производить обновление). Например, оператор не может
запрашивать ряды из более чем одной таблицы или содержать условие GROUP
BY
,
а также обычно он обязан запрашивать ключевые значения таблицы. Дополнительно о
конструировании обновляемых запросов см. документацию производителя БД.
Когда курсоры используются для внесения изменений в Вашу
БД, Вы всегда должны работать в рамках явной транзакции. Вы делаете это через
использование методов beginTransaction
,
commitTransaction
и rollbackTransaction
, как указано в
разделе "Обслуживание Транзакций." Если в таких ситуациях
Вы не используете явные транзакции, Вы можете получать ошибки из Вашей БД.
Например, Informix и Oracle возвращают сообщения об ошибке, если Вы используете
курсор без явной транзакции. Oracle возвращает Error ORA-01002: fetch out of sequence
;
Informix возвращает Error -206
: There is no current row for UPDATE/DELETE cursor
.
Как сказано в разделе "Навигация с Помощью Курсоров", Вы не обязательно привязаны к позиции в курсоре. Исходя из этого, при внесении изменений в БД не забывайте проверять, в каком ряду Вы работаете, прежде чем изменять его.
Также запомните, что при создании курсора указатель позиционируется перед рядом
курсора.
Так, чтобы обновить ряд, Вы обязаны вызвать метод next
как минимум
один раз для установки указателя на первый ряд таблицы. После этого Вы можете
присваивать значения столбцам курсора.
В следующем примере обновляемый курсор вычисляет премию для продавцов, выполнивших норму. Затем этой информацией обновляется БД:
connobj.beginTransaction ();
emps = connobj.cursor(
"select * from employees where dept='sales'", true);
// Прежде чем продолжить, убедитесь, что курсор действительно был возвращён
// и не было ошибки БД.
if ( emps && (connobj.majorErrorCode() == 0) ) {
// Производится итерация по рядам курсора с обновлением информации на базе
// return-значения функции metQuota.
while ( emps.next() ) {
if (metQuota (request.quota, emps.sold)) {
emps.bonus = computeBonus (emps.sold);
}
else emps.bonus = 0;
emps.updateRow ("employees");
}
// После выполнения - закрывается курсор и подтверждается транзакция.
emps.close();
connobj.commitTransaction();
}
else {
// Если курсор для работы отсутствовал, транзакция откатывается.
connobj.rollbackTransaction();
}
Этот пример создаёт обновляемый курсор для всех employees/служащих департамента Sales.
Производится итерация по рядам курсора через использование определяемой
пользователем функции JavaScript metQuota
, для того чтобы
определить, выполнил ли служащий норму. Эта функция использует значение свойства quota
объекта request
(возможно, установленное в форме на клиентской
странице) и столбец sold
курсора для выполнения этого определения.
Код затем устанавливает соответствующую премию и вызывает updateRow
для модифицирования таблицы employees
. Когда пройдены все ряды
курсора, подтверждается транзакция. Если вызов метода cursor
не
вернул никакого курсора, транзакция откатывается.
Помимо метода updateRow
, Вы можете использовать методы insertRow
и
deleteRow
для вставки нового ряда или удаления текущего. При
использовании deleteRow
не нужно присваивать никакого значения,
поскольку этот метод просто удаляет весь ряд.
Если Вы используете insertRow
, значения,
присваиваемые столбцам, используются для нового ряда. Если перед этим Вы вызвали
метод next
курсора, то текущие значения в ряду используются для
столбцов без присвоенных значений; иначе столбцы будут иметь значения null.
Также, если некоторые столбцы таблицы не вошли в курсор, insertRow
вставляет null в эти столбцы. Место расположения вставляемого ряда зависит от
библиотеки производителя БД. Если Вам нужен доступ к ряду после вызова метода insertRow
,
Вы обязаны сначала закрыть имеющийся курсор, а затем открыть новый.
В
DB2 имеется тип данных Time
. JavaScript не имеет соответствующего
типа данных.
Поэтому Вы не можете обновлять ряды значениями, использующими тип данных DB2 Time
.
Транзакция это несколько действий с базой данных, выполняемых вместе. Либо все эти действия выполняются успешно, либо все вместе терпят неудачу. Если эти действия выполняют (окончательно) изменения БД, говорится, что транзакция подтверждена. Вы можете также выполнить откат /roll back транзакции, т.е. не подтвердить её; это отменяет все выполненные действия.
Транзакции важны для поддержания целостности и структуры данных. В то время как различные серверы БД реализуют механизм транзакций с некоторыми отличиями, сервис LiveWire Database Service предоставляет одни и те же методы для обслуживания транзакций на всех БД. Проверьте в документации продавца БД информацию о согласованности данных и уровнях изоляции транзакций.
Вы можете использовать явный контроль транзакции над выполнением любого набора
действий. Например, акции, модифицирующие БД, должны проходить под управлением
транзакций. Это акции, соответствующие операторам SQL INSERT
,
UPDATE
и DELETE
. Транзакции могут также использоваться
для контролирования согласованности данных.
Для большинства БД, если Вы не выполняете явного управления транзакциями, машина выполнения использует фоновый механизм БД - autocommit/автоподтверждение, когда каждый оператор в БД рассматривается как отдельная транзакция. Каждый оператор подтверждается или откатывается немедленно на основе успеха или неуспеха выполнения каждого отдельного оператора. Явное управление транзакциями переопределяет работу этого механизма по умолчанию.
В некоторых БД, таких как Oracle, autocommit это явный механизм, который LiveWire
включает для каждого отдельного оператора. В других БД, таких как Informix, autocommit
это поведение по умолчанию, если Вы не создаёте транзакцию. В общем, LiveWire
скрывает эти различия и переводит приложение в режим autocommit, если приложение
не использует beginTransaction
для явного старта транзакции.
Для ANSI-БД Informix, LiveWire не использует autocommit.
Для этих БД приложение всегда использует транзакции, даже если никогда явно не
вызывает beginTransaction
.
Приложение обязано использовать commitTransaction
или rollbackTransaction
для завершения транзакции.
Настоятельно советуем всегда использовать явный контроль транзакций при
выполнении изменений в БД. Это гарантирует, что изменения будут сделаны или
отменены вместе. Кроме того, при использовании обновляемых курсоров Вы также
всегда должны использовать явные транзакции для контроля за целостностью Ваших
данных в период между чтением данных (с помощью next
) и их
изменением (с помощью insertRow
,
updateRow
или deleteRow
).
Как указано в разделе "Изменение Информации Базы Данных",
использование явного контроля транзакций и обновляемых курсоров необходимо для
того, чтобы исключить ошибки в некоторых БД, таких как Oracle и Informix.
Используйте следующие методы объектов database
или Connection
для явного управления транзакциями:
beginTransaction
стартует новую транзакцию. Все действия,
модифицирующие базу данных, группируются в данную транзакцию, называемую
текущей транзакцией.commitTransaction
подтверждает текущую транзакцию. Этот метод
пытается подтвердить все действия, выполненные после последнего вызова метода beginTransaction
.rollbackTransaction
откатывает текущую транзакцию. Этот метод
отменяет все изменения, сделанные с момента последнего вызова метода beginTransaction
.NO
LOG
,
не поддерживает транзакции, и Вы получите ошибку при использовании данных
методов.
Сервис LiveWire Database Service не поддерживает вложение транзакций. Если Вы
вызовете beginTransaction
несколько раз до подтверждения или отката
первой открытой Вами транзакции, Вы получите ошибку.
Для объекта database
максимум области
видимости транзакции ограничен текущим клиентским запросом (HTML-страницей) в
приложении. Если приложение существует на странице до вызова метода commitTransaction
или rollbackTransaction
, то транзакция автоматически подтверждается
или откатывается на основе установок параметра
commitflag
, задаваемого при соединении с БД.
Для объектов Connection
область видимости транзакции ограничена
периодом существования этих объектов. Если Вы освобождаете соединение или
закрываете пул соединений до вызова методов
commitTransaction
или rollbackTransaction
, то
транзакция автоматически подтверждается или откатывается на основе установок
параметра
commitflag
, задаваемого при соединении с БД методом connect
или в конструкторе DbPool
.
Если текущая транзакция отсутствует (то есть, если приложение не вызывало
beginTransaction
), вызовы методов commitTransaction
и rollbackTransaction
могут привести к ошибке в БД.
Транзакция может работать с разными объёмами данных. Пример из раздела "Изменение Информации Базы Данных" создаёт одну транзакцию для модифицирования всех рядов курсора. Если в Вашем курсоре небольшое количество рядов, такой подход будет оправданным.
Если, однако, Ваш курсор возвращает тысячи рядов, Вы можете обработать этот курсор в нескольких транзакциях. Такой подход снизит размер транзакций и улучшит доступ к информации.
Если Вы разбиваете Ваш процесс на несколько транзакций, убедитесь, что вызов
next
и ассоциированный вызов updateRow
или deleteRow
происходят внутри одной транзакции. Если Вы получаете ряд в одной транзакции,
завершаете её, а затем пытаетесь обновить или удалить ряд, Вы можете
получить ошибку в БД.
Выбор способа обработки транзакции зависит от целей Вашего приложения. Нужно обратиться к документации создателя БД для получения информации о том, как использовать транзакции для данного типа БД.
Двоичные данные для мультимедиа-содержимого, такого как изображение или звуковой файл, хранятся в БД в виде большого двоичного объекта/binary large object (BLOb). Можно использовать технику двух видов для обработки бинарных данных в приложениях JavaScript:
Blob
.
Если Вам не нужно хранить BLOb-данные в БД,
Вы можете хранить в БД имена файлов и осуществлять доступ к этим файлам в Вашем
приложении с помощью стандартных тэгов HTML. Например, если Вы хотите вывести
изображение в каждом ряду таблицы БД, Вы можете создать в таблице столбец с
названием
imageFileName
, содержащий имя нужного файла изображения. Затем
можно использовать такое выражение HTML
для показа изображения в каждом ряду:
<IMG SRC=`mycursor.imageFileName`>
Когда курсор проходит по таблице, имя файла в тэге IMG
изменяется
на ссылку на соответствующий файл.
Для того чтобы Вы могли манипулировать реальными
двоичными данными в Вашей БД, машина выполнения JavaScript распознаёт значения
столбца, являющиеся BLOb-данными. То есть,
когда программа создаёт объект Cursor
, если один из столбцов
таблицы БД содержит
BLOb-данные, программа создаёт Blob
-объект для соответствующего
значения в объекте
Cursor
. Вы можете затем использовать методы Blob
-объектов для отображения этих данных. Также, если нужно вставить BLOb-данные в
БД, программа предоставляет Вам для использования глобальную функцию.
В таблице показаны методы и функции для работы с BLOb-данными.
Метод blobImage
вызывает BLOb из БД, создаёт временный файл
специфицированного формата и генерирует HTML-тэг IMG
, который
ссылается на временный файл.
Машина выполнения удаляет временный файл после генерации страницы и отправки её
клиенту.
Метод blobLink
вызывает BLOb-данные из БД,
создаёт временный файл и генерирует гипертекстовую ссылку HTML на этот временный
файл. Машина выполнения удаляет временный файл после того как пользователь
щёлкнет на ссылке или через 60 секунд после того как запрос будет выполнен.
Следующий пример иллюстрирует использование blobImage
и blobLink
для создания временных файлов. В данном случае таблица FISHTBL
содержит 4 столбца: ID(ентификатор), name/имя и два изображения. Одно из них
является уменьшенной копией/thumbnail изображения; другое - большим изображением.
Код записывает HTML для отображения имени, уменьшенной копии и ссылки на большое
изображение.
cursor = connobj.cursor ("select * from fishtbl");
if ( cursor && (connobj.majorErrorCode() == 0) ) {
while (cursor.next()) {
write (cursor.name);
write (cursor.picture.blobImage("gif"));
write (cursor.picture.blobLink("image\gif", "Link" + cursor.id));
write ("<BR>");
}
cursor.close();
}
Если FISHTBL
содержит ряды для 4 рыб, пример может дать на выходе
такой HTML:
Cod <IMG SRC="LIVEWIRE_TEMP9">
<A HREF="LIVEWIRE_TEMP10">Link1 </A> <BR>
Anthia <IMG SRC="LIVEWIRE_TEMP11">
<A HREF="LIVEWIRE_TEMP12">Link2 </A> <BR>
Scorpion
<IMG SRC="LIVEWIRE_TEMP13">
<A HREF="LIVEWIRE_TEMP14">Link3 </A>
<BR>
Surgeon <IMG SRC="LIVEWIRE_TEMP15">
<A HREF="LIVEWIRE_TEMP16">Link4 </A> <BR>
Если Вам нужно добавить BLOb-данные в БД, используйте глобальную функцию blob
.
Она вводит
BLOb-данные в столбец в обновляемом курсоре. В противоположность blobImage
и
blobLink
, функция blob
является функцией верхнего
уровня, а не методом.
Следующие операторы вставляют BLOb-данные в столбцы ряда, а затем обновляют этот
ряд таблицы
FISHTBL
в БД. В курсоре имеется единственный ряд.
// Начало транзакции.
database.beginTransaction();
// Создание курсора.
fishCursor = database.cursor ("select * from fishtbl where
name='Harlequin Ghost Pipefish'", true);
// Убедимся, что курсор создан.
if ( fishCursor && (database.majorErrorCode() == 0) ) {
// Позиционируем указатель на ряд.
rowexists = fishCursor.next();
if ( rowexists ) {
// Присваиваем/вставляем blob-данные.
fishCursor.picture = blob ("c:\\data\\fish\\photo\\pipe.gif");
// Обновляем ряд.
fishCursor.updateRow ("fishtbl");
// Закрываем курсор и подтверждаем изменения.
fishCursor.close();
database.commitTransaction();
}
else {
// Иначе закрываем курсор и выполняем откат транзакции.
fishCursor.close();
database.rollbackTransaction();
}
}
else {
// Иначе вообще не получаем курсор; откатываем транзакцию.
database.rollbackTransaction();
}
Помните, что backslash (\) это escape-символ в JavaScript. Исходя из этого, Вы обязаны использовать двойной обратный слэш в именах файлов NT, как было в данном примере.
Хранимые процедуры являются неотъемлемой частью работы с реляционной БД. Они являются средством автоматизации часто выполняемых процессов, но их использование даёт также и некоторые другие преимущества:
Сервис LiveWire Database Service
предоставляет два класса для работы с хранимыми процедурами: Stproc
и Resultset
. используя методы этих классов, Вы можете вызывать
хранимые процедуры и манипулировать результатами их работы.
Работа хранимых процедур имеет отличия на разных БД, поддерживаемых сервисом LiveWire Database Service. Самое важное для LiveWire - это отличия в передаче информации в и из хранимой процедуры в приложении на JavaScript. Вы всегда используете параметры ввода хранимой процедуры для передачи информации в хранимую процедуру.
Концептуально имеются несколько способов, которыми можно запросить информацию из хранимой процедуры. Не всегда производитель БД даёт возможность запрашивать информацию любым их этих способов.
Хранимая процедура может выполнять один или более операторов SELECT
,
запрашивая информацию из БД. Вы можете представить эту информацию как
виртуальную таблицу, очень похожую на курсор "только для чтения".
(О курсорах см. раздел "Манипулирование Результатами
Выполнения Запросов с Помощью Курсоров").
LiveWire использует класс Resultset
как
контейнер рядов, возвращаемых одним оператором SELECT
хранимой
процедуры. Если хранимая процедура допускает наличие нескольких операторов SELECT
,
Вы получите отдельные объекты Resultset
для каждого оператора SELECT
.
Метод resultSet
класса Stproc
используется для
получения результирующего набора объектов, а затем методы этих объектов
используются для манипулирования результирующим набором.
БД различных производителей возвращают результирующий набор по-разному:
SELECT
.RESUME
, хранимая процедура может иметь набор этих return-значений.
Это набор напоминает ряды таблицы. LiveWire создаёт один результирующий набор
для вмещения этой виртуальной таблицы.SELECT
. Вы можете открыть несколько ref-курсоров в
хранимой процедуре Oracle, чтобы вместить ряды , возвращаемые разными
операторами SELECT
. LiveWire создаёт отдельные
Resultset
-объекты для каждого ref-курсора.Помимо стандартных параметров ввода, некоторые производители БД разрешают вводить другие типы параметров для хранимых процедур. Параметры вывода хранят информацию при возвращении из хранимой процедуры и параметры ввода/вывода, передающие и возвращающие информацию.
Для большинства БД Вы используете методы outParamCount
и outParameters
класса Stproc
для доступа к параметрам вывода и ввода/вывода. Informix,
однако, не разрешает параметры вывода и ввода/вывода. Соответственно, Вы не
должны использовать методы outParamCount
и outParameters
с хранимыми процедурами Informix.
Как и вызов функции, хранимая процедура может иметь возвращаемое/return значение. Для Oracle и Sybase это return-значение является дополнением к возвращаемому результирующему набору.
Метод returnValue
класса Stproc
используется для
доступа к
return-значению. Однако return-значения для хранимой процедуры Informix
используются для генерации её результирующего набора. Поэтому returnValue
всегда возвращает null для хранимых процедур Informix. Помимо этого, return-значения
недоступны для хранимых процедур ODBC и DB2.
После установки соединения с БД этапы использования хранимой процедуры в Вашем приложении несколько различаются для разных БД:
resultSet
-объект и получаются данные из этого
объекта.Заметьте, что для разных БД Вы можете завершить выполнение Вашей хранимой процедуры получением return-значения или доступом к параметрам вывода. После того как одно их этих двух действий выполнено, Вы не можете больше работать с результирующим набором, созданным при выполнении хранимой процедуры.
В следующих разделах эти этапы рассматриваются по отдельности.
Этот этап выполняется только в DB2.
В DB2 имеются различные системные таблицы, в которые Вы можете записать Вашу хранимую процедуру. В общем, вставка хранимой процедуры в эти таблицы не обязательна. Однако, для того чтобы использовать Вашу хранимую процедуру с LiveWire, Вы обязаны создать вхождения в этих таблицах. Этот этап выполняется вне приложения JavaScript.
Для обычного DB2-сервера Вы обязаны создать системную
таблицу DB2CLI.PROCEDURES
и ввести в неё Ваши DB2-хранимые
процедуры. DB2CLI.PROCEDURES
это таблица-псевдокаталог.
Если Ваш DB2 предназначен для IBM MVS/EA версии 4.1 или
более поздней, Вы обязаны определить имена Ваших хранимых процедур в
таблице-каталоге SYSIBM.SYSPROCEDURES
.
Не забывайте, что Вы используете C, C++ или другой язык для написания DB2-хранимой
процедуры. Типы данных, которые Вы используете в этих языках, не соответствуют
типам данных, доступным в DB2. Следовательно, если Вы добавляете хранимую
процедуру в DB2CLI.PROCEDURES
или в SYSIBM.SYSPROCEDURES
,
убедитесь, что записаны соответствующие типы данных DB2 для параметров хранимой
процедуры, а не типы данных исходных языков.
Информацию о типах данных DB2 и о том, как сделать вхождения в таблицах, см. в документации по DB2.
Этот этап относится только к пользовательским и системным хранимым процедурам DB2, ODBC и Sybase. Вам не нужно определять прототип хранимых процедур БД Oracle или Informix.
Для DB2, ODBC и Sybase программа не может определить в процессе выполнения,
предназначен определённый параметр для ввода, вывода, или для того и другого.
Соответственно, после того как Вы подключились к БД, Вы обязаны создать
прототип, предоставляющий информацию о хранимой процедуре, которую Вы хотите
использовать, через метод storedProcArgs
объекта database
или DbPool
.
Вам нужно использовать по одному прототипу для каждой хранимой процедуры Вашего приложения. Программа игнорирует дополнительные прототипы для одной и той же процедуры.
В прототипе Вы предоставляете имя хранимой процедуры и тип каждого из её
параметров.
Параметр обязан быть: для ввода/input (IN
), вывода/output (OUT
),
а для ввода и вывода -
(INOUT
).
Например, чтобы создать прототип для хранимой процедуры
newhire
, имеющей два параметра ввода и один параметр вывода, можно
использовать такой вызов метода:
poolobj.storedProcArgs("newhire", "IN", "IN", "OUT");
Этот этап применяется ко всем хранимым процедурам.
Для выполнения хранимой процедуры Вы создаёте Stproc
-объект,
используя метод storedProc
объектов database
или Connection
.
Создание такого объекта автоматически вызывает хранимую процедуру. При создании
объекта хранимой процедуры Вы специфицируете имя процедуры и любые параметры
процедуры.
Например, у Вас есть хранимая процедура newhire
, принимающая
параметры - строку и целое число. Следующий вызов метода создаёт spObj
-объект хранимой процедуры и вызывает хранимую процедуру newhire
:
spObj = connobj.storedProc("newhire", "Fred Jones", 1996);
В общем, Вы обязаны предоставить значения для всех параметров ввода/вывода для
хранимой процедуры. Если хранимая процедура имеет значение по умолчанию,
определённое для одного из её параметров, Вы можете использовать директиву "/Default/"
для специфицирования этого значения по умолчанию. Аналогично, если хранимая
процедура может принимать null-значение одного из своих параметров, Вы можете
специфицировать это null-значение либо директивой
"/Null/"
, либо передав само null-значение.
Например, предположим, хранимая процедура demosp
принимает два
строковых параметра и один целочисленный. Вы можете предоставить эти параметры
таким образом:
spobj = connobj.storedProc("demosp", "Param_1", "Param_2", 1);
Альтернативно, чтобы передать null для второго параметра и использовать значение по умолчанию для третьего параметра, Вы можете использовать один из следующих операторов:
spobj = connobj.storedProc("demosp", "Param_1", "/Null/", "/Default/");
spobj = connobj.storedProc("demosp", "Param_1", null, "/Default/");
В Informix значения по умолчанию обязаны
появляться только после всех специфицированных значений. Например, Вы не можете
использовать /Default/
для второго параметра процедуры, а затем
специфицировать значение для третьего параметра.
Вы можете также использовать директивы "/Default/"
и "/Null/"
для параметров ввода/вывода.
В Oracle хранимая процедура может принимать ref -курсоры
как параметры input/output или как output-параметры. Например, у Вас имеется
хранимая процедура Oracle под названием proc1
, принимающая 4
параметра: ref -курсор, целочисленное значение, другой ref -курсор и другое
целочисленное значение.
Вызов этой хранимой процедуры из SQL Plus может выглядеть так:
execute proc1 (refcursor1
, 3, refcursor2
, 5);
Однако, если Вы вызываете эту процедуру из приложения JavaScript, Вы не предоставляете ref -курсор-параметры. Вместо этого эквивалент может быть таким:
spobj = connobj.storedProc("proc1", 3, 5);
О параметрах вывода см. "Работа с Параметрами Вывода." Параметры вывода не могут быть null; однако Вы можете присвоить null-значение параметрам ввода или ввода/вывода.
В таблице дано резюме по методам объекта хранимой процедуры.
Stproc
- методыЭтот этап применяется ко всем хранимым процедурам.
Как указано в разделе "Результирующие наборы", разные БД
возвращают результирующие наборы разными способами. Например, у Вас имеется
таблица CUSTINFO
со столбцами
id
, city
и name
. В Sybase Вы можете
использовать такую процедуру для получения первых 200 рядов таблицы:
create proc getcusts as
begin
select id, name, city
from custinfo where custno < 200
end
Если CUSTINFO
является таблицей Informix, эквивалентная процедура в Informix
может быть:
create procedure getcusts returning int, char(15), char(15);
define rcity, rname char (15);
define i int;
foreach
select id, name, city into i, rname, rcity
from custinfo
where id < 200;
return i, rname, rcity with resume;
end foreach;
end procedure;
Если CUSTINFO
- таблица Oracle, эквивалентная процедура Oracle
может быть:
create or replace package orapack as
type custcurtype
is ref cursor return custinfo%rowtype
end orapack;
create or replace
custresultset (custcursor inout orapack.custcurtype)
as begin
open custcursor
for select id, name, city from custinfo
where id < 200
end custresultset;
Во всех случаях Вы создаёте resultSet
-объект для получения информации из хранимой процедуры. Вы делаете это через
использование метода resultSet
объекта хранимой процедуры так:
resObj = spObj.resultSet();
Как и для Cursor
-объектов, resultSet
-объекты
содержат текущий ряд, то есть ряд, на котором стоит указатель в результирующем
наборе. Вначале указатель позиционирован перед первым рядом результирующего
набора. Чтобы увидеть значения рядов результирующего набора, Вы используете
метод next
для перемещения указателя по рядам результирующего
набора, как показано в следующем примере:
spobj = connobj.storedProc("getcusts");
if ( spobj && (connobj.majorErrorCode() == 0) ) {
// Создаётся новый resultSet
-объект.
resobj = spobj.resultSet();
// Перед тем как продолжить, убедитесь, что Вы получили результирующий набор.
if ( resobj && (connobj.majorErrorCode() == 0) ) {
// Сначала перемещает указатель resultSet-объекта
к первому
// ряду результирующего набора, а затем циклически проходит по рядам.
while (resObj.next())
{
write("<TR><TD>" + resObj.name + "</TD>");
write("<TD>" + resObj.city + "</TD>");
write("<TD>" + resObj.id + "</TD></TR>");
}
resobj.close();
}
}
До тех пор, пока в результирующем наборе имеется следующий ряд, метод next
возвращает
true
и перемещает указатель к следующему ряду. Если указатель
достиг последнего ряда результирующего набора, метод next
возвращает false
.
Предыдущий пример работает с хранимой процедурой Sybase. В этом случае resultSet
-объект содержит именованное свойство для каждого столбца результирующего набора.
Для процедур Informix и DB2, по контрасту, объект не содержит именованных
столбцов. В этом случае Вы можете получить значения, ссылаясь на позицию столбца.
Так, для Informix и DB2 Вы можете использовать такой код для вывода аналогичной
информации:
spobj = connobj.storedProc("getcusts");
if ( spobj && (connobj.majorErrorCode() == 0) ) {
// Создаётся новый resultSet
-объект.
resobj = spobj.resultSet();
// Перед тем как продолжить, убедитесь, что Вы получили результирующий набор.
if ( resobj && (connobj.majorErrorCode() == 0) ) {
// Сначала перемещает указатель resultSet-объекта
к первому
// ряду результирующего набора, а затем циклически проходит по рядам.
while (resObj.next())
{
write("<TR><TD>" + resObj[1] + "</TD>");
write("<TD>" + resObj[2] + "</TD>");
write("<TD>" + resObj[0] + "</TD></TR>");
}
resobj.close();
}
}
Вы можете использовать позицию столбца для результирующих наборов любой БД, а не только с Informix и DB2. Вы можете использовать имя столбца для хранимой процедуры всех типов БД, а не только Informix или DB2.
Хранимая процедура Sybase, Oracle, DB2 или ODBC может
создавать несколько результирующих наборов. В этом случае хранимая процедура
предоставляет один resultSet
-объект для каждого набора.
Предположим, Ваша хранимая процедура выполняет такие операторы SQL:
select name from customers where id = 6767
select * from orders where id = 6767
Вы можете использовать несколько resultSet
-объектов, генерируемых
этими операторами, таким образом:
// Этот оператор нужен для DB2, ODBC и Sybase.
poolobj.storedProcArgs("GetCustOrderInfo","IN");
spobj = connobj.storedProc("GetCustOrderInfo",6767);
if ( spobj && (connobj.majorErrorCode() == 0) ) {
resobj1 = spobj.resultSet();
// Перед тем как продолжить, убедитесь, что результирующий набор существует.
if ( resobj1 && (connobj.majorErrorCode() == 0) ) {
// Первый результирующий набор возвращает только один ряд.
// Убедитесь, что ряд содержит данные.
rowexists = resobj1.next();
if ( rowexists )
write("<P>Customer " + resobj1.name +
" has the following orders:</P>");
resobj1.close();
// Второй результирующий набор возвращает один ряд для каждого заказа,
// помещённого пользователем. Убедитесь, что ряды содержат данные.
resobj2 = spobj.resultSet();
var i = 0;
if ( resobj2 && (connobj.majorErrorCode() == 0) ) {
write("\nOrder# Quantity Total</P>");
while(resobj2.next()) {
write(resobj2.orderno + " " + resobj2.quantity
+ " " + resobj2.Totalamount + "</P>");
i++;
}
resobj2.close();
write("Customer has " + i + " orders.</P>");
}
else write("Customer has
no orders.</P>");
}
}
spobj.close();
В качестве примера использования нескольких ref-курсоров Oracle
в хранимой процедуре см. описание класса Resultset
в книге
Серверный
JavaScript. Справочник
.
В таблице дано резюме по методам и свойствам класса Resultset
.
Resultset
- методы и свойства
resultSet
-объект является объектом "только
для чтения"/read-only, объектом последовательного стиля/sequential-style.
Исходя из этого, класс не имеет методов insertRow
, deleteRow
и
updateRow
, определённых для Cursor
-объектов.
Объект resultSet
не является бесконечно действующим. Вообще, когда
хранимая процедура стартует,
не допускается никакое взаимодействие между клиентом БД и сервером БД, пока
хранимая процедура не завершит выполнение. Есть три ситуации, когда
результирующий набор является недействующим:
database.beginTransaction();
spobj = database.storedProc("getcusts");
resobj = spobj.resultSet();
database.commitTransaction();
// Неверно! Результирующий набор больше не действует!
col1 = resobj[0];
resultSet
-объекты до
вызова методов returnValue
или outParameters
объекта
хранимой процедуры. Если Вы вызвали один из этих методов, Вы не сможете больше
получать данные из результирующего набора и не сможете получить дополнительные
результирующие наборы. См. о работе этих методов раздел "Работа
с Return-Значениями".
spobj = database.storedProc("getcusts");
resobj = spobj.resultSet();
retval = spobj.returnValue();
// Неверно! Результирующий набор больше не действует!
col1 = resobj[0];
resultSet
-объекты до вызова
методов cursor
или SQLTable
ассоциированного
соединения. Как только Вы вызовете cursor
или
SQLTable
, результирующий набор станет недоступен. Например,
следующий код неверен:
spobj = database.storedProc("getcusts");
resobj = spobj.resultSet();
curobj = database.cursor ("select * from orders");
// Неверно! Результирующий набор больше не доступен!
col1 = resobj[0];
resultSet
-объектами до вызова методов cursor
или SQLTable
ассоциированного соединения.
В ODBC, если Вы получаете курсор, выполняете доступ к результирующему набору,
а затем используете курсор,
Cursor
-объект становится недоступным. Например, следующий код
неверен:
spbobj = database.storedProc("getcusts");
resobj = spobj.resulSet();
curobj = database.cursor ("select * from orders");
col1 = resobj[0];
// Неверно! Курсор больше не доступен.
curobj.next();
Этот этап относится к хранимым процедурам Sybase и Oracle. Для процедур Informix, ODBC
и DB2 метод returnValue
всегда возвращает null.
Если Ваша хранимая процедура возвращает значение (return value), Вы можете
получить к нему доступ с помощью метода returnValue
.
В DB2, ODBC и Sybase Вы обязаны использовать хранимые
процедуры и курсоры последовательно. Вы не можете их перемешивать. Исходя из
этого, Вы обязаны дать системе знать, что Вы закончили использование хранимой
процедуры, прежде чем сможете работать с курсором. Это выполняется через вызов
метода returnValue
объекта хранимой процедуры. Этот метод выдаёт return-значение
хранимой процедуры (если она его имеет) и завершает выполнение хранимой
процедуры. Вы должны также закрыть все объекты, относящиеся к хранимым
процедурам, когда завершаете их использование.
Для DB2, ODBC и Sybase Вы обязаны запросить resultSet
-объекты до вызова метода returnValue
. После того как Вы вызвали returnValue
,
Вы больше не сможете получить данные из результирующего набора и не сможете
получить какие-либо дополнительные результирующие наборы. Вы должны вызывать returnValue
после того, как обработали результирующий набор, но до запроса параметров вывода.
Этот этап касается хранимых процедур Sybase, Oracle, DB2 или ODBC. Для процедур Informix методы, обсуждаемые здесь, не применяются.
Чтобы определить количество параметров вывода процедуры (включая параметры и
вывода, и ввода/вывода), Вы используете метод outParamCount
. Вы
можете работать с параметрами вывода хранимой процедуры, используя метод outParameters
объекта. Если outParamCount
возвращает 0, хранимая процедура не
имеет параметров вывода. В этой ситуации не вызывайте
outParameters
.
Например, предположим, Вы создали хранимую процедуру, которая находит фамилию служащего по заданному ID. Если имеется фамилия служащего, ассоциированная с данным ID, процедура возвращает 1, и её output-параметр содержит фамилию служащего. Иначе параметр вывода является пустым. Следующий код выводит фамилию служащего или сообщение о том, что фамилия не найдена:
id = 100;
getNameProc = connobj.storedProc("getName", id);
returnValue = getNameProc.returnValue();
if (returnValue == 1)
write ("Name of employee is " + getNameProc.outParameters(0));
else
write ("No employee with id = " + id);
Предположим, хранимая процедура имеет один параметр ввода, один параметр ввода/вывода и один параметр вывода. Далее примем, что вызов хранимой процедуры отсылает значение параметра ввода и параметра ввода/вывода, как показано здесь:
spobj = connobj.storedProc("myinout", 34, 56);
Метод outParameters
возвращает любые параметры ввода/вывода до
того как возвратит первый параметр вывода.
В предыдущем примере, если Вызывается
outParameters(1)
, возвращается значение, возвращаемое хранимой
процедурой. И наоборот,
если вызывается outParameters(0)
, метод возвращает 56. Это
значение, переданное хранимой процедуре в позиции параметра ввода/вывода.
Параметры вывода не могут быть null; однако Вы можете присвоить null-значение
параметра ввода или ввода/вывода.
В DB2, ODBC и Sybase Вы обязаны запрашивать resultSet
-объекты и использовать метод returnValue
до того, как вызываете outParameters
.
После того как Вы вызвали returnValue
или outParameters
,
Вы больше не сможете получить данные из результирующего набора и не сможете
получить какие-либо дополнительные результирующие наборы. Вы должны вызывать outParameters
после обработки результирующего набора и любых return-значений.
Хранимые процедуры
Informix и Sybase могут возвращать коды ошибки, используя механизм исключений.
После того как Вы запустили процедуру на выполнение, Вы можете запрашивать эти
коды ошибок и сообщения об ошибках, используя методы majorErrorCode
и majorErrorMessage
ассоциированного объекта database
или
Connection
.
Например, у Вас имеется хранимая процедура Informix:
create procedure usercheck (user varchar(20))
if user = 'LiveWire' then
raise exception -746, 0, 'User not Allowed';
endif
end procedure
Если Вы запустите эту процедуру на выполнение, Вы сможете проверять, появилась ли ошибка, а затем получить доступ к коду ошибки и сообщению о ней:
spobj = connobj.storedProc("usercheck");
if ( connobj.majorErrorCode() ) {
write("The procedure returned this error
code: " +
connobj.majorErrorCode());
write("The procedure returned this error message: " +
connobj.majorErrorMessage());
}
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.