В этой главе обсуждается использование службы LiveWire Database Service для соединения Вашего приложения с реляционными базами данных DB2, Informix, ODBC, Oracle или Sybase. Показано, как выбрать наилучшую методологию для Вашего приложения.
В главе имеются следующие разделы:
Ваше JavaScript-приложение, запущенное на сервере Netscape Enterprise Server, может использовать LiveWire Database Service для доступа к БД серверов Informix, Oracle, Sybase и DB2 и серверов, использующих стандарт Open Database Connectivity (ODBC). Ваше приложение, запущенное на сервере Netscape FastTrack Server, может получать доступ только к БД на серверах, использующих ODBC.
В последующих обсуждениях предполагается, что Вы уже знакомы реляционными БД и Structured Query Language (SQL).
Прежде чем создать приложение JavaScript с использованием LiveWire, база или базы данных, к которым Вы планируете подключаться, должны уже существовать на сервере БД. Также Вы должны знать их структуру. Если Вы создаёте совершенно новое приложение, включающее БД, необходимо создать БД и заполнить её данными (как минимум в форме прототипа) до создания приложения.
До того как Вы попытаетесь использовать LiveWire, убедитесь, что Ваша рабочая
среда сконфигурирована соответственно. О том, как конфигурировать, см.
Главу 10, "Конфигурирование Вашей Базы Данных." Вы можете также использовать
приложение-образец videoapp
, описанное в
Главе 13, "Приложения-Образцы Videoapp и Oldvideo," для изучения некоторых
возможностей LiveWire.
Обычно для того чтобы взаимодействовать с БД, необходимо выполнить следующие общие действия:
database
или создать объект DbPool
для установки пула соединений БД. Это обычно выполняется на начальной странице
приложения, если только оно не требует установления специального соединения.database
или явно - при использовании метода connection
объекта DbPool
.Connection
).В этой главе обсуждаются первые три пункта действий. В Главе 9, "Работа с БД", обсуждаются остальные шаги.
Есть два основных способа соединения с БД с помощью сервиса LiveWire Database Service.
Это объекты DbPool
и Connection
, либо объект database
.
При этом подходе Вы создаёте пул соединений для работы
с реляционной БД. Вы создаёте экземпляр класса DbPool
,а затем
получаете доступ к объектам Connection
через этот объект DbPool
.
Объекты DbPool
и Connection
распределяют между собой
работу по соединению с БД и обслуживанию набора соединений и доступ к БД через соединение.
Это весьма гибкий подход. Ваше приложение может иметь несколько пулов соединений,
каждый со своей собственной конфигурацией БД и пользователя. Каждый пул может
иметь несколько соединений при такой конфигурации. Это даёт одновременный доступ
к нескольким БД или к одной БД из нескольких бюджетов. Вы можете также
ассоциировать пул непосредственно с приложением, а не с отдельным клиентским
запросом, и иметь таким образом транзакции, захватывающие несколько клиентских
запросов. Это ассоциирование выполняется путём присвоения пула свойству
объекта project
и удаления этого присвоения после окончания работы с пулом.
При этом подходе Вы используете предопределённый объект database
для соединения с БД при наличии единственной конфигурации соединения БД и
пользователя. Объект database
выполняет все действия по работе с БД.
Можно представить этот объект как database
единый пул соединений с БД.
Этот подход несколько проще, так как используется только один объект database
,
а не несколько объектов DbPool
и Connection
. Однако
при этом теряется гибкость первого подхода. Если Вы используете только объект database
и хотите соединиться с разными БД или разными бюджетами, Вы обязаны отключиться
от одной конфигурации, для того чтобы подключиться к другой. Также, при
использовании объекта database
, одна транзакция не может захватить
несколько клиентских запросов, а соединения с несколькими БД-источниками не могут быть установлены одновременно.
Как описано в последующих разделах, Вы должны ответить на два основных вопроса, когда решаете, как устанавливать соединения с БД:
В таблице резюмируется, как ответ на эти вопросы влияет на установку и обслуживание пула соединений с БД и отдельных соединений. В последующих разделах обсуждаются детали этих вариантов.
Сколько конфигураций БД? | Где соединение с пулом? | Где отключение пула? | Какой объект(ы) содержит пул? | Должен ли Ваш код хранить пул и соединение? |
Как Ваш код хранит пул и соединение в объекте project ? |
---|---|---|---|---|---|
Зависит от 1 |
Если Вы хотите использовать объект database
, Вам не нужно создавать
его. Это предопределённый объект, предоставляемый машиной выполнения JavaScript.
Если Вам нужны дополнительные возможности класса DbPool
, Вы
создаёте экземпляр класса DbPool
и соединяете этот объект с
конкретной БД, которая создаёт пул соединений.
Вы можете создать общий DbPool
-объект и специфицировать позднее
информацию соединения (используя метод connect
), или можете
специфицировать информацию соединения при создании пула. Общий DbPool
-объект
не имеет никаких доступных соединений в момент его создания. Исходя из этого,
Вам может понадобиться установить соединение при создании этого объекта. Если Вы
используете объект database
, Вы всегда обязаны устанавливать
соединение путём вызова метода database.connect
.
connect (dbtype, serverName, userName, password,
databaseName, maxConnections, commitFlag);
При создании соединения Вы можете специфицировать следующую информацию, либо при
создании DbPool
-объекта, либо при вызове метода connect
объекта DbPool
или database
:
dbtype
: Тип БД. Обязан быть "DB2"
, "INFORMIX"
, "ODBC"
, "ORACLE"
или "SYBASE"
. (Для приложений, запущенных на сервере Netscape FastTrack Server,
обязан быть "ODBC"
.)serverName
: Имя сервера БД, с которым устанавливается соединение.
Имя сервера обычно назначается при установке БД. В случае сомнений, узнайте у
администратора БД или системы.username
: Имя пользователя, соединяющегося с БД.password
:
Пароль пользователя.databaseName
: Имя БД на данном сервере, с которой устанавливается
соединение. Если Ваш сервер баз данных поддерживает множество БД на одном
сервере, предоставьте имя используемой БД. Если предоставлена пустая строка,
производится соединение с БД по умолчанию. Для Oracle, ODBC и DB2 Вы всегда
обязаны предоставлять пустую строку.maxConnections
: (Optional/Необязательный Параметр) Допустимое
количество соединений в пуле БД. Помните, что клиентская лицензия Вашей БД,
возможно, специфицирует максимальное количество соединений. Не устанавливайте
этот параметр в число, превышающее количество, допустимое по лицензии. Если Вы
не предоставляете этот параметр для объекта DbPool
, он имеет
значение 1. Если Вы не предоставляете этот параметр для объекта database
,
его значением будет то, что Вы специфицировали в Application Manager как
значение для Built-in Maximum Database Connections/Встроенное Значение
Максимального Количества Соединений с БД, когда устанавливали приложение. (См. "Установка
Нового Приложения"). См. в разделе "Однопоточные и
Многопоточные БД" о том, что Вы должны предусмотреть при установке этого
параметра.commitflag
: (Необязательный Параметр) Булево значение,
указывающее, подтвердить ли открытую транзакцию или выполнить её откат при
закрытии соединения. Специфицируйте true
для подтверждения
открытой транзакции и false
- для выполнения отката. Если этот
параметр Вами не предоставлен для объекта DbPool
, его значением
будет false
. Если этот параметр не предоставлен для объекта database
,
значением параметра будет true
.Например, следующий оператор создаёт новый пул БД из 5 соединений с БД Oracle. В этом пуле неподтверждённые транзакции откатываются:
pool = new DbPool ("ORACLE", "myserver1", "ENG", "pwd1", "", 5);
Приложение-образец dbadmin
позволяет Вам экспериментировать с
соединениями с различными БД как разным пользователям.
Для многих приложений Вы, возможно, захотите выполнять совместное использование набора соединений несколькими клиентами, или чтобы соединение захватывало несколько клиентских запросов. В этих случаях Вы должны устанавливать соединение на начальной странице Вашего приложения. Это позволит исключить возможные проблемы в тех случаях, когда отдельные клиенты выполняют совместные соединения с БД.
Однако для некоторых приложений каждый клиент должен выполнять своё собственное соединение. Как сказано в разделе "Совместное Использование Массива Пулов Соединений," клиенты могут совместно использовать объекты. Если это так, убедитесь, что блокировки используются для управления совместным использованием данных, как указано в разделе "Безопасное Использование Объектов с Помощью Блокировки".
В следующей таблице показаны методы объектов DbPool
и database
для обслуживания пула соединений. (Объект database
использует
другие методы, рассмотренные ранее, для работы с соединением с БД.)
DbPool
и database
для
обслуживания пулов соединенийLiveWire поддерживает многопоточный доступ к БД. То есть она поддерживает наличие более чем одного потока доступа к одной БД в единицу времени. Отсюда ясно, для чего нужен пул соединений с более чем одним соединением. Однако библиотеки БД некоторых производителей не являются многопоточными. Для таких БД не имеет значения, сколько соединений имеется в Вашем пуле, так как только одно соединение может устанавливаться с БД в единицу времени.
В этой таблице дан список клиентских библиотек баз данных, которые являются многопоточными на указанных платформах.
Sybase | Informix | Oracle | DB2 | ODBC 1 | |
---|---|---|---|---|---|
1
Все многопоточные тесты для ODBC были сделаны на MS SQL Server. Если Вы используете другой драйвер ODBC, узнайте у производителя, является ли драйвер многопоточным. |
Эти указания являются критичными для однопоточного доступа. Однако Вы должны
думать об этом даже тогда, когда используете БД с многопоточным доступом.
Однопоточная библиотека БД может иметь существенные
ограничения производительности. Поскольку только один поток имеет доступ к БД в
единицу времени, все прочие потоки обязаны ждать, когда первый поток освободит
соединение с БД, прежде чем один из них сможет получить доступ к БД. Если
доступа ожидают одновременно несколько потоков, каждому придётся ожидать довольно долго.
При разработке доступа к БД Вы должны предусмотреть следующее:
Каждый поток обязан ждать окончания работы другого потока. Чем короче время взаимодействия с БД, тем меньше ожидание.
Ограничения на использование транзакций для клиентских библиотек БД, которые не являются многопоточными:
database()
или DbPool(), Вы обязаны
установить максимальное допустимое количество соединений с БД в Вашем
приложении большим, чем количество клиентов, которые, как Вы предполагаете,
будут использовать Ваше приложение. Иначе некоторые клиенты не смогут
установить соединение с БД, и их приложения зависнут.
В любой данный момент времени соединённый объект DbPool
или database
и все соединения пула ассоциированы с определённой конфигурацией базы данных. То
есть всё, что находится в пуле, соединено с определённым сервером БД как
отдельный пользователь с отдельным паролем и с определённой БД.
Если Ваше приложение всегда использует одну конфигурацию, то можно использовать
единственный объект DbPool
или использовать объект database
и соединяться однократно. В этом случае Вы должны выполнить соединение на
начальной странице Вашего приложения.
Если Вашему приложению требуется несколько конфигураций, или потому что оно обязано соединяться с несколькими БД, или с одной БД, но с разными пользователями, или и то, и другое, Вам необходимо определить, как обслуживать эти конфигурации.
Если Вы используете объект database
и несколько соединений, то
выбора у Вас нет. Вы обязаны соединяться, отсоединяться и повторно соединяться с
объектом database
каждый раз, когда Вам нужно изменить что-либо в
конфигурации. Вы делаете это под управлением клиентских запросов. В этой
ситуации убедитесь, что используются блокировки, как указано в разделе "Совместное
Использование Объектов с Блокировкой," чтобы получать исключительный доступ
к объекту database
. Иначе другой клиентский запрос может отключить
объект до того, как текущий клиентский запрос закончит с ним работу. Хотя Вы и
можете использовать объект database
таким образом, лучше будет
всё-таки использовать объекты DbPool
.
Если Вы используете объекты DbPool
и несколько конфигураций, Вы
также должны соединяться, отсоединяться и повторно соединяться с объектом DbPool
.
Однако с объектами DbPool
у Вас появится больше возможностей. Вы
можете создавать столько пулов, сколько нужно, и ставить их под контроль объекта project
. (См.
в Главе 6, "Обслуживание Сессий" информацию
об объекте project
.) Использование нескольких пулов более
эффективно и обычно надёжнее, чем многократное использование единственного пула (как
с объектом database
, так и с единственным объектом DbPool
).
При определении того, как обслуживать пулы, Вы обязаны учитывать два фактора: ко скольки конфигурациям будут иметь доступ пулы и нужно ли будет одному соединению захватывать несколько клиентских запросов. Если у Вас небольшое количество возможных конфигураций, Вы можете создать отдельные пулы для каждой. В разделе "Совместное Использование Фиксированного Набора Пулов Соединений" обсуждается этот подход.
Если у Вас имеется очень большое или заранее не известное количество конфигураций (например, если все пользователи БД получают свои индивидуальные ID), нужно предусмотреть два варианта. Если одного соединения достаточно для выполнения одного клиентского запроса, Вы можете создавать отдельные пулы на клиентской странице.
Однако иногда соединение должно захватывать несколько клиентских запросов (например, если одна транзакция в БД захватывает несколько клиентских запросов). Возможно также, что Вы просто не хотите повторно соединяться с БД на каждой странице приложения. Если это так, Вы можете создать массив пулов, который используется совместно. В разделе "Совместное Использование Массива Пулов Соединений" обсуждается этот подход.
Независимо от используемого подхода, если Вам больше не нужно отдельное
соединение в пуле, зачистите ресурсы, используемые этим соединением, чтобы оно
стало доступным для других пользователей. Чтобы выполнить это, закройте все
открытые курсоры, хранимые процедуры и результирующие наборы. Верните соединение
обратно в пул. (Вы не должны освобождать соединение, если используете объект database
.)
Если Вы не освободили соединение, то при попытке отсоединить пул система будет ожидать, перед тем как реально отсоединиться, возникновения одного из двух условий:
Если Вы создаёте отдельные пулы БД для каждого пользователя, убедитесь, что пул отсоединён, до того как закончить с ним работу. О курсорах см "Манипуляции с Результатами Выполнения Запросов с Помощью Курсоров". О хранимых процедурах и результирующих наборах см. "Вызов Хранимых Процедур".
Часто в приложении небольшой набор пулов соединений используется всеми
пользователями данного приложения. Например, Вашему приложению нужно соединяться
с тремя различными БД или с одной БД, которая использует 4 пользовательских ID,
соответствующих 4 разным департаментам. Если у вас имеется небольшой набор
возможных конфигураций соединения, Вы можете создать отдельный пул для каждой
конфигурации. Для этого используйте объекты DbPool
.
Тогда необходимо, чтобы пул работал в течение всего периода существования
приложения, а не просто в течение периода существования клиента или отдельного
клиентского запроса. Вы можете реализовать это, создав каждый пул БД как
свойство объекта project
. Например, начальная страница приложения
может содержать эти операторы:
project.engpool = new DbPool ("ORACLE", "myserver1", "ENG",
"pwd1", "", 5, true);
project.salespool = new DbPool ("INFORMIX", "myserver2", "SALES",
"pwd2", "salsmktg", 2);
project.supppool = new DbPool ("SYBASE","myserver3","SUPPORT",
"pwd3", "suppdb", 3, false);
Эти операторы создают три пула для различных групп пользователей приложения.
Пул project.eng
содержит 5 соединений Oracle и подтверждает любую
неподтверждённую транзакцию при высвобождении соединения обратно в пул.
Пул project.sales
имеет два соединения Informix и откатывает любую
неподтверждённую транзакцию при окончании соединения.
Пул project.supp
имеет три соединения Sybase и откатывает любую
неподтверждённую транзакцию при окончании соединения.
Вы должны создавать такой пул как часть начальной страницы приложения. Эта
страница выполняется только при старте приложения. На страницах, доступных
пользователям, Вы не создаёте пул и не изменяете соединение. Вместо этого эти
страницы определяют, к какой группе принадлежит текущий пользователь, и
используют уже установленное соединение из соответствующего пула. Например, в
следующем коде определяется, какую БД использовать (на основе значения свойства userGroup
объекта request
), в БД ищется некоторая информация, которая
выводится пользователю, а затем соединение освобождается:
if (request.userGroup == "SALES") {
salesconn = project.salespool.connection("A sales connection");
salesconn.SQLTable ("select * from dept");
salesconn.release();
}
Вы можете также создать пул и изменить соединение на странице, доступной пользователю. В этом случае Вы должны быть осторожны, так как несколько пользователей, осуществляющих одновременный доступ к этой странице, не должны мешать друг другу. Например, только один пользователь должен иметь возможность создавать пул, используемый всеми остальными пользователями. О надёжности совместного использования информации см. раздел "Надёжное Совместное Использование Объектов с Помощью Блокировки".
В разделе
"Совместное Использование Фиксированного Набора Пулов
Соединений" описано, как Вы можете применить свойства объекта project
для совместного использования фиксированного набора пулов соединений. Этот
подход используется, если Вам в процессе разработки уже известно количество
необходимых пулов соединений и Вам нужно только небольшое количество соединений.
Часто нельзя предугадать заранее количество необходимых пулов соединений. В других случаях это возможно, но это количество недопустимо велико. Например, предположим, что для каждого потребителя, имеющего доступ к Вашему приложению, приложение проверяет пользовательский профиль на предмет определения того, какую информацию из БД вывести. Вы можете дать каждому потребителю уникальный пользовательский идентификатор ID для БД. Такое приложение требует, чтобы каждый пользователь имел свой набор параметров соединений (соответствующий различным пользовательским ID в БД) и, соответственно, разные пулы соединений.
Вы можете создать объект DbPool
и соединять и отсоединять его на
каждой странице приложения. Это будет работать, только если одно соединение не
должно захватывать несколько клиентских запросов. Иначе эта ситуация должна
обрабатываться по-разному.
Для данного приложения, вместо создания фиксированного набора пулов соединений
на начальной странице приложения или пула на каждой клиентской странице, Вы
создаёте одно свойство объекта project
, которое будет содержать
массив пулов соединений. Доступ к элементам этого массива осуществляется по
ключу на базе определённого пользователя.
Во время инициализации Вы создаёте массив, но не помещаете в него элементы (поскольку
никто ещё не пытался использовать приложение), как показано здесь:
project.sharedPools = new Object();
Когда пользователь впервые стартует приложение, оно получает идентифицирующий
пользователя ключ. На основе этого ключа приложение создаёт объект пула DbPool
и сохраняет его в массиве пулов. Имея данный пул соединений, оно может либо
соединяться на каждой странице, либо устанавливать соединение так, как описано в
разделе "Обслуживание Соединения по Запросам." Следующий
код создаёт пул либо получает уже созданный, проверяет его соединение и работает
затем с БД:
// Генерируется уникальный индекс для обращения к данному клиенту, если это
// ещё не было сделано на другой странице. О функции ssjs_generateClientID см.
// "Уникальное Обращение к Объекту client".
if client.id == null {
client.id = ssjs_generateClientID();
}
// Если пула для данного клиента ещё нет, он создаётся
// и производится его соединение с БД.
project.lock();
if (project.sharedPools[client.id] == null)
{
project.sharedPools[client.id] = new DbPool ("ORACLE",
"myserver", user, password, "", 5, false);
}
project.unlock();
// Для удобства устанавливается переменная для этого пула.
var clientPool = project.sharedPools[client.id];
// Теперь у Вас есть пул: посмотрим, соединён ли он. Если нет, попытаемся соединить его.
// Если это не удаётся, перенаправляем на специальную страницу,
// чтобы проинформировать пользователя.
project.lock();
if (!clientPool.connected()) {
clientPool.connect("ORACLE", "myserver", user, password,
"", 5, false);
if (!clientPool.connected()) {
delete project.sharedPools[client.id];
project.unlock();
redirect("noconnection.html");
}
}
project.unlock();
// Если Вы дошли до этого места, Вы успешно соединились и
// можете работать с БД.
clientConn = clientPool.connection();
clientConn.SQLTable("select * from customers");
// ... другие операции с БД ...
// Всегда освобождайте соединение, если оно Вам больше не нужно.
clientConn.release();
}
Когда пользователь в следующий раз войдёт в приложение (например, с другой
страницы приложения), он использует тот же самый код и получит сохранённый пул
соединений и (возможно, сохранённый,) объект Connection
из объекта project
.
Если Вы используете ssjs_generateClientID
и сохраняете ID в объекте client
,
Вам может понадобиться защита от вторжения через доступ к этому ID и,
следовательно, к закрытой информации.
Объект sharedConns
, использованный в этом примере кода, не является
предопределённым объектом
JavaScript. Он просто создан в этом примере и может иметь другое имя по Вашему
выбору.
Как только Вы создали пул соединений, клиентская страница может получить доступ
к индивидуальному соединению из пула. Если Вы используете объект database
,
соединение в данном объекте является неявным; то есть Вы используете методы
объекта database
для доступа к соединению. Если, однако, Вы
используете объекты DbPool
, соединение инкапсулируется в объекте Connection
,
который Вы получаете через вызов метода объекта DbPool
. Например, в
следующем пуле:
project.eng = new DbPool ("ORACLE", "myserver", "ENG", "pwd1", "", 5);
Вы можете получить соединение из пула с помощью такого вызова метода:
myconn = project.eng.connection ("My Connection", 60);
Оба параметра метода являются необязательными. Первый это имя соединения (используется
при отладке); второй это целое число, обозначающее таймаут в секундах. В этом
примере, если пул имеет доступное соединение или если оно становится доступным в
течение 60 секунд, это соединение присваивается переменной myconn
.
Если соединение не становится доступным в течение указанного периода, этот метод
возвращается без соединения. Дополнительно об ожидании получения соединения из
пула см. раздел "Ожидание Соединения". О том, что делать,
если соединение не получено, см. "Запрашивание Незанятого
Соединения".
Если Вы закончили использование соединения, возвратите его в пул путём вызова
метода release
объекта Connection
. (Если Вы
используете объект database
, Вам не нужно самостоятельно
освобождать соединение). Прежде чем вызвать метод release
, закройте
все открытые курсоры, хранимые процедуры и результирующие наборы. Если Вы
вызываете метод release
, система ожидает, когда всё закроется, и
возвращает затем соединение в пул базы данных. После этого соединение доступно
следующему пользователю. Об использовании курсоров см. "Манипуляции
с Результатами Запросов с Помощью Курсоров". О хранимых процедурах и
результирующих наборах см. "Вызов Хранимых Процедур".
После получения соединения (через объект database
или объект Connection
),
Вы можете работать с БД. В таблице резюмированы методы объектов database
и connection
для работы с единственным соединением. Объект database
имеет и другие методы для обслуживания пула соединений, рассмотренные в разделе "Обслуживание
Пулов Соединений."
database
и Connection
для Работы с Единственным СоединениемВ некоторых случаях может понадобиться, чтобы единственное соединение захватывало несколько клиентских запросов. То есть Вы сможете использовать одно соединение на нескольких страницах HTML.
Обычно вы используете свойства объекта client
для информации,
захватывающего клиентские запросы. Однако значение свойства объекта client
не может быть объектом. Исходя из этого, Вы не можете сохранять пул соединений
БД в объекте client
. Вместо этого Вы используете пул соединений,
хранимый в объекте project
, обслуживая их так, как описано в данном
разделе. Если вы используете этот подход, Вам может понадобиться кодирование
пользовательской информации, по соображениям безопасности.
Будьте особенно осторожны при использовании такого подхода, поскольку сохранение соединения таким способом делает его недоступным для других пользователей. Если все соединения окажутся недоступны, новые запросы будут ожидать явного освобождения соединения или таймаута соединения. Это особенно проблематично для однопоточных библиотек БД. (Об установлении соединений так, что они будут запрашиваться, если не заняты в течение продолжительного времени, см. "Запрашивание Незанятого Соединения").
В следующем примере соединение и транзакция
захватывают несколько клиентских запросов. Код сохраняет соединение как свойство
объекта sharedConns
, который сам является свойством объекта project
.
Объект sharedConns
не является предопределённым объектом JavaScript.
Он просто создан в данном примере и может иметь другое имя, по Вашему выбору.
Поскольку один пул используется всеми клиентами, Вы должны создавать объект sharedConns
и создавать и соединять сам пул на начальной странице приложения примерно таким
кодом:
project.sharedConns = new Object();
project.sharedConns.conns = new Object();
project.sharedConns.pool = new DbPool ("SYBASE", "sybaseserver",
"user", "password", "sybdb", 10, false);
Затем на первой клиентской странице, получающей доступ к пулу, следуйте такой стратегии:
// Генерируется уникальный индекс для обращения к данному клиенту, если он ещё
// не сгенерирован на другой странице.
if client.id == null {
client.id = ssjs_generateClientID();
}
// Для удобства устанавливается переменная для данного пула.
var clientPool = project.sharedConns.pool;
// Проверяется, соединён ли пул. Если нет, перенаправляется
// на специальную страницу для информирования пользователя.
project.lock();
if (!clientPool.connected()) {
delete project.sharedConns.pool;
project.unlock();
redirect("noconnection.html");
}
project.unlock();
// Соединение получается из пула и сохраняется в объекте project.
project.sharedConns.conns[client.id] = clientPool.connection();
var clientConn = project.sharedConns.conns[client.id];
clientConn.beginTransaction();
cursor = clientConn.cursor("select * from customers", true");
// ... другие операции с БД ...
cursor.close();
}
Заметьте, что эта страница не выполняет откат или подтверждение транзакции.
Соединение остаётся открытым, и транзакция продолжается. (Транзакции
рассматриваются в разделе "Обслуживание Транзакций").
Вторая HTML-страница запрашивает соединение, базируясь на значении client.id
,
и продолжает работать с БД так:
// Запрашивается соединение.
var clientConn = project.sharedConns.conns[client.id];
// ... Выполняются ещё какие-нибудь операции с БД ...
// Здесь, если операции с БД успешно прошли, okay устанавливается в 1.
// Если была ошибка при работе с БД, okay устанавливается в 0. В конце
// подтверждается или откатывается транзакция на основе этого значения.
if (okay)
clientConn.commitTransaction();
else
clientConn.rollbackTransaction();
// Соединение возвращается в пул.
clientConn.release();
// Избавляемся от значения свойства объекта. Оно Вам больше не нужно.
delete project.sharedConns.conns[client.id];
В этом примере объект sharedConns
сохраняет единственный объект DbPool
и соединения для данного пула, которые используются в данный момент. Ситуация
может быть значительно сложнее. Если у Вас имеется фиксированный набор пулов БД,
Вы можете определить отдельный объект для хранения соединений каждого пула.
Если у вас имеется массив пулов и каждому пулу необходимы соединения,
захватывающие несколько запросов, Вам необходимо создать массив объектов, каждый
из которых сохраняет пул и массив его соединений. Как ответвление, вместо
немедленного перенаправления в том случае, если пул не соединён, клиентская
страница может сделать новую попытку установить соединение.
Если Вы используете ssjs_generateClientID
и храните ID в объекте client
,
Вам понадобится защита от вторжения и получения доступа к ID и, следовательно, к
закрытой информации.
В пуле, созданном объектом DbPool
, соединений имеется фиксированное
количество соединений. Если в момент попытки доступа все соединения заняты, Ваше
приложение ожидает освобождения соединения в течение специфицированного периода
таймаута. Вы можете управлять периодом ожидания.
Предположим, Вы определили следующий пул из 3 соединений:
pool = new DbPool ("ORACLE", "myserv", "user", "password", "", 3);
Предположим далее, что три клиента одновременно получают доступ к приложению и каждый использует одно из трёх соединений. Четвёртый клиент теперь запрашивает соединение через следующий вызов:
myconnection = pool.connection();
Этот клиент обязан ждать, пока один из трёх клиентов не освободит соединение. В
данном случае, поскольку вызов connection
не специфицирует таймаут,
клиент ждёт освобождения соединения неопределённо долго, а затем возвращает это
соединение.
Вы можете специфицировать различные периоды таймаута, задавая аргументы метода connection
.
Второй аргумент метода connection
это период таймаута в секундах.
Если Вы специфицируете таймаут 0, система ждёт бесконечно долго. Например,
следующий код ожидает соединения только 30 секунд перед таймаутом:
myconnection = pool.connection ("Name of Connection", 30);
Если в течение специфицированного периода соединение не освобождается, метод
возвращает null, и в сообщение об ошибке устанавливается сообщение о наименьшей
ошибке. Вы можете получить это сообщение, вызвав метод minorErrorMessage
объекта pool
. Если Вы вызываете таймаут из connection
,
Вам может понадобиться освободить соединение, отключив одно из уже установленных.
Дополнительно см. "Запрашивание Свободного Соединения".
Если Ваше приложение запрашивает соединение из объекта DbPool
, оно
может не получить его. Доступные опции в этот момент зависят от архитектуры
Вашего приложения.
Если каждое соединение существует только в период существования отдельного клиентского запроса, недоступность соединений невозможна из-за того, что пользователь оставил приложение в бездействии на значительный период времени. Это может произойти только из-за того, что весь код страницы JavaScript не закончил выполняться. В этом случае Вы не должны пытаться разорвать используемое соединение, чтобы воспользоваться им. Если Вы разорвёте соединение в этот момент, Вы рискуете оставить этот поток выполнения в несоответствующем состоянии. Вместо этого Вы должны удостовериться, что Ваше приложение освобождает каждое соединение по мере завершения его использования. Если Вы не хотите ожидать соединения, Вы должны предоставить пользователю возможность выбора другого варианта.
Если, наоборот, соединение захватывает несколько клиентских запросов, Вы можете захотеть запросить свободные соединения. В этой ситуации соединение может освободиться, поскольку пользователь не завершил транзакцию. Например, предположим, что пользователь отправляет данные на первой странице приложения и эти данные начинают многостраничную транзакцию БД. Вместо отправки данных для продолжения транзакции на следующей странице, пользователь переходит на другой сайт и никогда не вернётся к данному приложению. По умолчанию соединение остаётся открытым и не может использоваться другими клиентами.
Вы можете вручную запросить соединение, зачистив его и освободив в пул БД. Чтобы сделать это, напишите функции типа нижеследующих:
Bucket
: Определяет тип объекта (в данном примере - с названием bucket
)
для содержания соединения и штампа времени.MarkBucket
: Помечает объект bucket
штампом текущего
времени.RetrieveConnections
: Проходит по массиву соединений в поисках
объекта Connection
, к которому не было доступа в течение
установленного лимита времени, и использует CleanBucket
(описан
далее) для запрашивания этого объекта.CleanBucket
: Закрывает курсоры (и возможные хранимые процедуры и
результирующие наборы), откатывает или подтверждает каждую открытую транзакцию
и возвращает соединение в пул.Ваше приложение может использовать эти функции так:
Bucket
для
создания объекта bucket
.MarkBucket
для обновления штампа времени.RetrieveConnection
для поиска незанятых соединений,
закройте все открытые курсоры, подтвердите или откатите работающие транзакции
и верните незанятые соединения обратно в пул.Также на каждой странице, где Ваше приложение использует соединение, необходимо убедиться, что другой поток освободил соединение, до того как данная страница будет достигнута данным клиентом.
Функция bucket содержит соединение и штамп времени. Этот образец конструктора функции принимает соединение в качестве единственного параметра:
// Конструктор для Bucket
function Bucket(c)
{
this.connection = c;
this.lastModified = new Date();
}
Вы можете вызвать эту функцию для создания bucket для соединения, когда Вы получаете соединение из пула соединений. Вы можете добавить другие свойства для соединения bucket. Например, Ваше приложение может содержать курсор, который захватывает клиентские запросы. Тогда Вы можете использовать свойство для добавления курсора в bucket, так чтобы можно было закрыть открытый курсор при запрашивании соединения. Вы сохраняете курсор в bucket во время его создания, как видно из следующего оператора:
myBucket.openCursor =
myBucket.connection.cursor("select * from customer", true);
Функция MarkBucket
принимает объект Bucket
в качестве параметра и устанавливает в поле lastModified
текущее
время.
function MarkBucket(bucket)
{
bucket.lastModified = new Date();
}
Вызывайте MarkBucket
на каждой странице приложения, которая
использует соединение, содержащееся в bucket. Это восстанавливает в lastModified
значение текущей даты и предотвращает появление незанятых соединений.
RetrieveConnections
сканирует массив объектов Bucket
,
ищет buckets соединения, штамп времени которых установлен ранее некоторого
определённого времени. Если соединение найдено, функция вызывает CleanBucket
(описан
далее) для возвращения соединения в пул БД.
// Запрашиваются соединения, не занятые в течение специфицированного количества минут.
function RetrieveConnections(BucketArray, timeout)
{
var i;
var count = 0;
var now;
now = new Date();
// Этот цикл выполняется для каждого bucket в массиве.
for (i in BucketArray) {
// Вычисляется разница во времени между текущей/now и последней/last
// модифицированной датой. Эта разница выражается в миллисекундах.
// Если она больше значения timeout, вызывается функция зачистки.
if ((now - i.lastModified)/60000) > timeout) {
CleanBucket(i);
// Избавляется от bucket, поскольку он больше не используется.
delete i;
count = count + 1;
}
}
return count;
}
После того как определено, что соединение должно быть
запрошено (с помощью функции RetrieveConnections
), Вам понадобится
функция для зачистки подробностей соединения и возврата его обратно в пул базы
данных.
Данная функция-образец закрывает открытые курсоры, откатывает открытые
транзакции и освобождает соединение.
function CleanBucket(bucket)
{
bucket.openCursor.close();
bucket.connection.rollbackTransaction();
bucket.connection.release();
}
CleanBucket
принимает, что данный bucket содержит открытый курсор
и его соединение имеет открытую транзакцию. Принимается также, что отсутствуют
хранимые процедуры и результирующие наборы. В Вашем приложении может
понадобиться и какая-нибудь другая проверка.
Следующий пример кода использует уже определённые функции для запрашивания соединений, к которым не обращались в течение 10 минут. Сначала создаётся совместно используемый массив соединений и пул БД с 5 соединениями:
if ( project.sharedConns == null ) {
project.sharedConns = new Object();
project.sharedConns.pool = new DbPool ("ORACLE", "mydb",
"user", "password", "", 5, false);
if ( project.sharedConns.pool.connected() ) {
project.sharedConns.connections = new Object();
}
else {
delete project.sharedConns;
}
}
Теперь используем следующий код для попытки получения соединения. После зачистки
пула генерируется клиентский ID, который затем используется как индекс в массиве
соединений. Далее пытаемся получить соединение. Если возникает таймаут, вызываем RetrieveConnections
для возвращения старого соединения в пул.
Если RetrieveConnections
возвращает соединение в пул, пытаемся
получить соединение вновь. Если всё ещё не можем получить соединение,
выполняется перенаправление на другую страницу с информацией, что свободных
соединений нет. Если запрашивается соединение, сохраняем его в новом bucket
соединения и сохраняем этот bucket соединения в совместно используемом массиве
соединений.
if ( project.sharedConns != null ) {
var pool = project.sharedConns.pool;
// Этот код запускается, только если пул уже соединён.
// Если нет, возможно, Вам нужен код для соединения.
if ( pool.connected() == true ) {
// Генерируется клиентский ID.
client.id = ssjs_generateClientID();
// Попытка получить соединение.
var connection = pool.connection("my connection", 30);
// Если соединение null, тогда ничего не будет доступно
// в течение специфицированного лимита времени. Пытаемся запросить старые соединения.
if (connection == null) {
// Запрашиваются соединения, не используемые в течение последних 10 минут.
var count = RetrieveConnections(project.sharedConns.connections, 10);
// Если count не равен 0, делаем какое-нибудь соединение доступным.
if (count != 0){
connection = pool.connection("my connection", 30);
// Если connection всё ещё null, отказываемся.
if (connection == null)
redirect("nofreeconnections.html");
}
else {
// Отказываемся.
redirect("nofreeconnections.html");
}}
// Если Вы не дошли досюда, Вы получили соединение и можете продолжить работу.
// Поместите это connection в новый bucket, стартуйте транзакцию,
// получайте курсор, сохраняйте его в bucket и продолжайте работу.
project.sharedConns.connections[client.id] =
new Bucket(connection);
connection.beginTransaction();
project.sharedConns.connections[client.id].cursor =
connection.cursor("select * from customer", true);
// Помечаем bucket соединения как использованный.
MarkBucket(project.sharedConns.connections[client.id]);
// Операторы Базы Данных.
...
}
На следующей странице многостраничной транзакции выполняются операции БД по этому соединению. После последней операции БД по соединению помечается bucket соединения:
var Bucket = project.sharedConns.connections[client.id];
if ( Bucket == null) {
// Повторное соединение.
}
else {
// Взаимодействие с БД.
...
// Последняя операция БД на странице.
row = Bucket.cursor.next();
row.customerid = 666;
Bucket.openCursor.insertRow("customer");
// Помечается bucket соединения как использованный на данной странице.
MarkBucket(Bucket);
}
Дата последнего обновления: 29 сентября 1999 г.
© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.