На главную страницу
Форум txt.version   




Статья :: Глава 9. Работа с БД.
Оглавление | Назад | Вперёд | Индекс

Глава 9
Работа с Базой Данных

В этой главе обсуждается работа с реляционными базами данных DB2, Informix, ODBC, Oracle и Sybase. Рассматривается, как запрашивать информацию из БД и использовать её в приложении, как работать с транзакциями и как выполнять хранимые процедуры.

Не забывайте, что, если Ваше приложение работает на Netscape FastTrack Server, а не на Netscape Enterprise Server, оно может иметь доступ только к серверам БД, использующим стандарт ODBC.

В главе имеются следующие разделы:

Взаимодействие с Реляционной Базой Данных


Сервис LiveWire Database Service даёт возможность взаимодействовать с реляционной БД различными способами.
Вы можете:

  • Выполнять запросы к БД и заставить машину выполнения автоматически форматировать результаты.
  • Использовать курсоры для выполнения запросов к БД и представлять результаты в манере приложения или использовать их при вычислениях.
  • Использовать курсоры для изменения информации в БД.
  • Использовать транзакции.
  • Выполнять процессинг SQL без помощи курсоров.
  • Запускать хранимые процедуры.

Об установке и обслуживании соединений БД см. Главу 8, "Соединение с Базой Данных".

Автоматическое Отображение Результатов Выполнения Запроса


Наиболее простым и быстрым способом отобразить результаты выполнения запросов к БД является использование метода SQLTable объекта database или объекта Connection. Метод SQLTable принимает оператор SQL SELECT и возвращает HTML-таблицу. Каждый ряд и столбец в запросе это ряд и столбец таблицы. Таблица HTML имеет также заголовочную ячейку для каждого столбца таблицы БД.

Метод SQLTable не даёт Вам возможности управлять форматированием вывода. Кроме того, если вывод содержит объект Blob, этот объект не выводится как изображение. (Об использовании blob см. раздел "Работа с Бинарными Данными"). если Вы хотите специализировать вывод, используйте курсор БД для создания Вашей собственной функции вывода/отображения. См. также "Манипуляции с Результатами Выполнения Запросов с Помощью Курсоров".

В качестве примера: если myconn это Connection -объект, следующий оператор JavaScript отображает результат выполнения запроса к БД в виде таблицы:

myconn.SQLTable("select * from videos");

Вот первая часть таблицы, которая могла бы быть сгенерирована этим оператором:
 

TitleID Year CategoryQuantity On Hand Synopsis

A Clockwork Orange

1

1975

Science Fiction

5

3

Little Alex and his droogies stop by the Miloko bar for a refreshing libation before a wild night on the town.

Philadelphia Story

1

1940

Romantic Comedy

Katherine Hepburn and Cary Grant are reunited on the eve of her remarriage, with Jimmy Stewart for complications.

Выполнение Произвольных Операторов SQL


Метод 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.

Таблица 9.1  Свойства и Методы Класса Cursor
Метод или Свойство Описание

colName

Свойства, соответствующие каждому столбцу курсора. Имя каждого свойства colName  это имя столбца в БД.

close

Закрывает курсор.

columns

Возвращает количество столбцов в курсоре.

columnName

Возвращает имя столбца в курсоре.

next

Делает следующий ряд курсора текущим рядом.

insertRow

Вставляет новый ряд в специфицированную таблицу.

updateRow

Обновляет записи в текущем ряду специфицированной таблицы.

deleteRow

Удаляет текущий ряд специфицированной таблицы.

Полную информацию об этих методах см. в описании класса Cursor в книге "Серверный JavaScript. Справочник".

Создание Курсора


Как только приложение установило соединение с БД, Вы можете создать курсор путём вызова метода cursor ассоциированного объекта database или Connection. Создание объекта Cursor также открывает курсор в БД. Вам не нужно выполнять отдельную команду open.

Можно предоставить следующую информацию при создании объекта Cursor:

  • Оператор SQL SELECT, поддерживаемый сервером БД. Чтобы гарантировать независимость от вида БД, используйте синтаксис SQL 89/92. Курсор создаётся как виртуальная таблица результата выполнения этого оператора SQL.
  • Необязательный Булев параметр указывает, является ли курсор обновляемым. Используйте этот параметр только в том случае, когда Вам нужно изменять содержимое БД, как указано в разделе "Изменение Информации Базы Данных". Не всегда возможно создать обновляемый курсор для каждого оператора 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 NAMECITY

1

Sally Smith

Suva

2

Jane Doe

Cupertino

3

John Brown

Harper's Ferry

В примере использованы методы, обсуждаемые в последующих разделах.

Отображение Выражений и Агрегатных Функций


Операторы 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();
}

Этот код даст на выходе:

Clockwork Orange

6/3/97

9/3/97

Philadelphia Story

8/1/97

8/5/97

Вы не всегда можете находиться в нужном месте в курсоре. Например, предположим, что Вы создаёте курсор и, пока Вы работаете с ним, кто-то добавляет ряд в таблицу. В зависимости от установок БД, этот ряд может появиться в Вашем курсоре. Исходя из этого, когда это удобно (как при обновлении рядов), Вы можете сделать так, чтобы Ваш код проверял, находится ли указатель в нужном ряду.

Работа со Столбцами


Метод 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.
  • Конечно, если Ваша БД не поддерживает транзакции, Вы не сможете использовать их. Например, БД Informix, созданная с использованием опции 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.

Если Вам не нужно хранить BLOb-данные в БД, Вы можете хранить в БД имена файлов и осуществлять доступ к этим файлам в Вашем приложении с помощью стандартных тэгов HTML. Например, если Вы хотите вывести изображение в каждом ряду таблицы БД, Вы можете создать в таблице столбец с названием imageFileName, содержащий имя нужного файла изображения. Затем можно использовать такое выражение HTML для показа изображения в каждом ряду:

<IMG SRC=`mycursor.imageFileName`>

Когда курсор проходит по таблице, имя файла в тэге IMG изменяется на ссылку на соответствующий файл.

Для того чтобы Вы могли манипулировать реальными двоичными данными в Вашей БД, машина выполнения JavaScript распознаёт значения столбца, являющиеся BLOb-данными. То есть, когда программа создаёт объект Cursor, если один из столбцов таблицы БД содержит BLOb-данные, программа создаёт Blob -объект для соответствующего значения в объекте Cursor. Вы можете затем использовать методы Blob -объектов для отображения этих данных. Также, если нужно вставить BLOb-данные в БД, программа предоставляет Вам для использования глобальную функцию.

В таблице показаны методы и функции для работы с BLOb-данными.

Таблица 9.2  Методы и Функции для Работы с Blob'ами
Метод или ФункцияОписание

blobImage

Метод, используемый при отображении BLOb-данных, хранимых в БД. Возвращает тэг HTML IMG для специфицированного типа изображения (GIF, JPEG и т.д.).

blobLink

Метод, используемый при создании ссылки, которая указывает на BLOb-данные гиперссылкой. Возвращает гиперссылку HTML на BLOb.

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 используется для получения результирующего набора объектов, а затем методы этих объектов используются для манипулирования результирующим набором.

БД различных производителей возвращают результирующий набор по-разному:

  • Хранимые процедуры Sybase могут напрямую возвращать результат выполнения одного или более операторов SELECT.
  • Хранимые процедуры Informix могут иметь несколько return-значений. Несколько return-значений подобны столбцам одного ряда таблицы, за исключение того, что эти столбцы именованы. Кроме того, если Вы используете возможность RESUME, хранимая процедура может иметь набор этих return-значений. Это набор напоминает ряды таблицы. LiveWire создаёт один результирующий набор для вмещения этой виртуальной таблицы.
  • Хранимые процедуры Oracle используют ref-курсоры для вмещения рядов, возвращаемых оператором SELECT. Вы можете открыть несколько ref-курсоров в хранимой процедуре Oracle, чтобы вместить ряды , возвращаемые разными операторами SELECT. LiveWire создаёт отдельные Resultset -объекты для каждого ref-курсора.
  • Хранимые процедуры DB2 используют открытые курсоры для возвращения результирующих наборов.

Параметры Вывода и  Ввода/Вывода


Помимо стандартных параметров ввода, некоторые производители БД разрешают вводить другие типы параметров для хранимых процедур. Параметры вывода хранят информацию при возвращении из хранимой процедуры и параметры ввода/вывода, передающие и возвращающие информацию.

Для большинства БД Вы используете методы outParamCount и outParameters класса Stproc для доступа к параметрам вывода и ввода/вывода. Informix, однако, не разрешает параметры вывода и ввода/вывода. Соответственно, Вы не должны использовать методы outParamCount и outParameters с хранимыми процедурами Informix.

Return-Значения


Как и вызов функции, хранимая процедура может иметь возвращаемое/return значение. Для Oracle и Sybase это return-значение является дополнением к возвращаемому результирующему набору.

Метод returnValue класса Stproc используется для доступа к return-значению. Однако return-значения для хранимой процедуры Informix используются для генерации её результирующего набора. Поэтому returnValue всегда возвращает null для хранимых процедур Informix. Помимо этого, return-значения недоступны для хранимых процедур ODBC и DB2.

Этапы Использования Хранимых Процедур


После установки соединения с БД этапы использования хранимой процедуры в Вашем приложении несколько различаются для разных БД:

  1. (Только DB2) Хранимая процедура регистрируется в соответствующих системных таблицах. (Это выполняется вне JavaScript.)
  2. (DB2, ODBC и Sybase) Определяется прототип для Вашей хранимой процедуры.
  3. (Все БД) Выполняется хранимая процедура.
  4. (Все БД) Создаётся resultSet -объект и получаются данные из этого объекта.
  5. (DB2, ODBC и Sybase) Выполнение завершается доступом к return-значению.
  6. (DB2, ODBC, Oracle и Sybase) Выполнение завершается получением параметров вывода.

Заметьте, что для разных БД Вы можете завершить выполнение Вашей хранимой процедуры получением 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-значение параметрам ввода или ввода/вывода.

В таблице дано резюме по методам объекта хранимой процедуры.

Таблица 9.3 Stproc - методы
Метод Описание

resultSet

Возвращает следующий результирующий набор для хранимой процедуры.

Для Informix Вы можете иметь нуль или более результирующих наборов. Для других БД Вы можете иметь нуль, один или более результирующих наборов.

returnValue

Запрашивает return-значение хранимой процедуры.

Для Informix, DB2 и ODBC этот метод всегда возвращает null.

outParameters

Возвращает специфицированный параметр вывода.

Поскольку хранимые процедуры Informix не используют параметры вывода, не используйте этот метод с Informix.

outParamCount

Возвращает количество параметров вывода.

Для Informix этот метод всегда возвращает 0, поскольку хранимые процедуры Informix не используют параметров вывода.

Работа с Результирующими Наборами


Этот этап применяется ко всем хранимым процедурам.

Как указано в разделе "Результирующие наборы", разные БД возвращают результирующие наборы разными способами. Например, у Вас имеется таблица 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.

Таблица 9.4 Resultset - методы и свойства
Метод или Свойство Описание

colName

Свойства, соответствующие каждому столбцу результирующего набора. Имя каждого свойства это имя столбца в БД.

Поскольку хранимые процедуры Informix и DB2 не возвращают именованных столбцов, эти свойства не создаются для хранимых процедур Informix или DB2.

columns

Возвращает количество столбцов результирующего набора.

Для Informix этот метод возвращает количество return-значений для одного ряда.

columnName

Возвращает имя столбца в результирующем наборе.

Поскольку хранимые процедуры Informix и DB2 не имеют ассоциированных имён столбцов, не используйте этот метод для хранимых процедур этих БД.

close

Удаляет Resultset -объектobject.

next

Делает следующий ряд результирующего набора текущим рядом. Возвращает false, если текущий ряд является последним рядом результирующего набора; иначе возвращает true.

resultSet -объект является объектом "только для чтения"/read-only, объектом последовательного стиля/sequential-style. Исходя из этого, класс не имеет методов insertRow, deleteRow и updateRow, определённых для Cursor -объектов.

Когда Можно Использовать Результирующие Наборы


Объект resultSet не является бесконечно действующим. Вообще, когда хранимая процедура стартует, не допускается никакое взаимодействие между клиентом БД и сервером БД, пока хранимая процедура не завершит выполнение. Есть три ситуации, когда результирующий набор является недействующим:

  1. Если Вы создаёте результирующий набор как часть транзакции, Вы обязаны закончить использование результирующего набора в течение выполнения этой транзакции. Если Вы подтвердили транзакцию или выполнили её откат, Вы не сможете получать данные из результирующего набора и не сможете получить дополнительные результирующие наборы. Например, следующий код не допускается:
  2. database.beginTransaction();
    spobj = database.storedProc("getcusts");
    resobj = spobj.resultSet();
    database.commitTransaction();
    // Неверно! Результирующий набор больше не действует!
    col1 = resobj[0];
  3. Для Sybase, ODBC и DB2 Вы обязаны запросить resultSet -объекты до вызова методов returnValue или outParameters объекта хранимой процедуры. Если Вы вызвали один из этих методов, Вы не сможете больше получать данные из результирующего набора и не сможете получить дополнительные результирующие наборы. См. о работе этих методов раздел "Работа с Return-Значениями".
  4. spobj = database.storedProc("getcusts");
    resobj = spobj.resultSet();
    retval = spobj.returnValue();
    // Неверно! Результирующий набор больше не действует!
    col1 = resobj[0];
  5. Для Sybase Вы обязаны запросить resultSet -объекты до вызова методов cursor или SQLTable ассоциированного соединения. Как только Вы вызовете cursor или SQLTable, результирующий набор станет недоступен. Например, следующий код неверен:
  6. spobj = database.storedProc("getcusts");
    resobj = spobj.resultSet();
    curobj = database.cursor ("select * from orders");
    // Неверно! Результирующий набор больше не доступен!
    col1 = resobj[0];
  7. Для ODBC соблюдаются несколько иные ограничения. Вы также обязаны работать с resultSet -объектами до вызова методов cursor или SQLTable ассоциированного соединения. В ODBC, если Вы получаете курсор, выполняете доступ к результирующему набору, а затем используете курсор, Cursor -объект становится недоступным. Например, следующий код неверен:
  8. spbobj = database.storedProc("getcusts");
    resobj = spobj.resulSet();
    curobj = database.cursor ("select * from orders");
    col1 = resobj[0]; // Неверно! Курсор больше не доступен.
    curobj.next();

Работа с Return-Значениями


Этот этап относится к хранимым процедурам 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


Хранимые процедуры 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. Все Права Зарезервированы.

Глава 9. Работа с БД.

страницы в данном разделе 
 Технология Разработки Приложений JavaScript.  Индекс.
 Оглавление.  Доступ к Сервису CORBA.
 Глава 11. Конвертация Типов Данных.  Глава 8. Соединение с БД.
 Глава 9. Работа с БД.  Конфигурирование Вашй Базы Данных.
 Обработка Ошибок в LiveWire.  Введение.
 Словарь.  Оглавление.
 JavaScript. Обзор.  Основы Серверного JavaScript.
 LiveConnect. Обзор.  Другая Функциональность JavaScript.
 

Часть 1. Разработка Серверных Приложений.

 Возможности Серверного JavaScript.
 Работа с LiveConnect.  Служба LiveWire Database Service.
 Предисловие.  Быстрое Начало с Примерами Приложений.
 Служба Session Management Service.  Оглавление-Индекс.
 Приложения-Образцы Videoapp и Oldvideo.   


Разделы
Околокомпьютерная литература (375)
Программирование (102)
Программы (75)
ОС и Сети (49)
Интернет (29)
Аппаратное обеспечение (16)
Базы данных (6)

Содержание сайта (выборка)
Apache
Протоколы TCP/IP (принципы, протоколы и архитектура)

PHP, PELR, JSP
PHP
JavaServer Pages (JSP)

Базы данных
Основы mysql
СУБД INFORMIX
СУБД POSTGRES
Основы проектирования реляционных баз данных

HTML, javascript
Спецификация HTML 4.01
Каскадные Таблицы Стилей, Уровень 2
Клиентский JavaScript. Справочник.
JavaScript руководство пользователя
Серверный JavaScript 1.4. Руководство по Использованию.

Паскаль, C, C++, C#
GCC (примеры)
FAQ Валентинa Озеровa DELPHI
C





 
©  programming-lang.com  справочник программиста