Для демонстрации трехуровневой архитектуры доступа к базам данных с использованием ПО промежуточного уровня была создана система, состоящая и следующих частей:
Интерфейс определен в виде IDL-модуля, содержащего
IDL-интерфейсы и IDL-структуры. Основным IDL-интерфейсом
является Database, который используется
для получения метаинформации и данных:
interface Database
{
SchemaInfo getSchemaInfo() raises (ServerException);
ObjectIdSeq getObjectIds(in string obTyName) raises (ServerException);
ObjectValue getObjectValue(in ObjectId obId) raises (ServerException);
};
Операция getSchemaInfo() возвращает в качестве результата
структуру SchemaInfo,содержащую описание схемы:
struct SchemaInfo
{
string name; // имя схемы
string longName; // имя схемы для пользователя
sequence objectTypeInfos;
};
Схема содержит множество объектных типов, каждый из которых
описывается структурой ObjectTypeInfo:
struct ObjectTypeInfo
{
string name; // имы типа
string longName;
sequence attributeInfos;
sequence singleRoleInfos;
sequence multiRoleInfos;
};
В объектном типе могут быть определены атрибуты, которые имеют
встроенный тип, например, целый, строковый и т.п. Атрибут
описывается в структуре AttributeInfo:
struct AttributeInfo
{
string name;
string longName;
string type;
};
В объектном типе также могут быть определены роли, которые
имеют тип ссылки на объектный тип или тип множества ссылок
на объектный тип. В первом случае роль описывается при
помощи структуры SingleRoleInfo,
во втором - помощи структуры MultiRoleInfo:
struct SingleRoleInfo
{
string name;
string longName;
string type;
};
struct MultiRoleInfo
{
string name;
string longName;
string type;
};
В структуре ObjectTypeInfo задается информация
обо всех атрибутах и ролях - как унаследованных, так
и определенных непосредственно в типе.
Операция Database::getObjectIds() в качестве
результата возвращает множество объектных идентификаторов,
соответствующих объектам объектного типа, имя которого задано
в качестве аргумента операции.
Объектный идентификатор представлен в виде структуры
ObjectId:
struct ObjectId
{
string key; // уникальный ключ, по которому однозначно определяется объект
string objectName; // имя объекта для представления пользователю
string className; // имя объектного типа
};
Операция Database::getObjectValue() в качестве
результата возвращает значение объекта, идентификаиор
которого задается при помощи параметра операции.
Значение объекта представляется в виде структуры
ObjectValue:
struct ObjectValue
{
sequence attributes;
sequence singleRoles;
sequence multiRoles;
};
Значения атрибутов и ролей передаются в том же порядке, в котором
были заданы описания атрибутов и ролей в структуре ObjectTypeInfo.
Для представления значений атрибутов и ролей используются
структуры AttributeValue,SingleRoleValue и MultiRoleValue:
struct AttributeValue
{
string name; // имя атрибута
string value; // значение атрибута (для упрощения используется
// представление значения в виде строки)
};
struct SingleRoleValue
{
string name; // имя роли
string value; // значение роли - название объекта
};
struct MultiRoleValue
{
string name; // имя роли
sequence value; // значение роли - множество объектных идентификаторов
};
Database.
Реализация адаптера может зависеть или не зависеть от схемы базы данных
- это определяется протоколом доступа к (мета)данных, который
предлагает СУБД. Независимость от схемы не означает, что
адаптер способен поддержать
Database, которая может использоваться как
базис для реализации конкретных адаптеров.
Схема тестовой базы данных использует наследование, которое отсутствует в Oracle. Эта проблема решается способом, описанном ниже. Для каждого объектного типа создается объектный тип Oracle, в котором помимо атрибутов, определенных в исходном объектном типе, добавлены нижеописанные сущности.
Описанный выше способ позволяет эмулировать множественное наследование в Oracle. В запросах необходимо использовать методы чтения, а не атрибуты.
Данный адаптер реализует простейший вид интеграции данных - объединение баз данных: каждому ресурсному объекту соответствует один "виртуальный" объект, и наоборот, каждому "виртуальному" объекту соответствует один ресурсный объект. Несколько объектных типов из разных схем могут отображаться в один "виртуальный" объектный тип, т.о. происходит объединение экстенсионалов типов.
"Виртуальные" объекты существуют временно: они создаются
только в реализации операции Database::getObjectValue().
Идентификтор "виртуального" объекта включает в себя информацию о
том, из какой базы данных получен его прообраз, а также
объектный идентификтор объекта-прообраза, т.о. адаптер
может определить к какому адаптеру следует обратиться
для получения значения ресурсного объекта.
В системе может быть несколько серверных приложений, каждое из которых может содержать один или несколько адаптеров для различных баз данных. При инициализации адаптер регистрируется в CORBA Naming Service.
При запуске клиентское приложение ищет в Naming Service зарегистрировавшиеся адаптеры и устанавливает с ними соединение. Далее, для каждого найденного адаптера клиент, используя универсальный протокол доступа,читает метаинформцию о схеме базы данных, и динамически создает графический интерфейс, специфичный для каждой схемы.
Графический интерфейс устроен следующим образом: в окне приложения есть две панели, левая панель содержит графический элемент управления в виде дерева, в котором узлами первого уровня являются базы данных; второй уровень - объектные типы, присутствующие в схеме базы данных; третий уровень занимают объекты соответствующего типа в соответствующей базе данных.
Когда пользователь выбирает какой-либо объект, в правой
панели появляется форма, в которой отображаются значения
атрибутов и ролей выделенного объекта.
Формы не предназначены для изменения данных, поэтому
в них используются лишь
графические элементы типа JLabel и списки
(JList).
Т.к. клиент использует универсальный протокол доступа, то его реализация не зависит от конкретных схем баз данных, т.е. клиент настраивается динамически на определённую схему.
В системе, где возможно, используются компоненты, не зависящие от конкретной схемы, например:
В документе также описан подход как обходить некоторые ограничения (отсутствие наследования) в существующей объектно-реляционной технологии в СУБД Oracle8.
Примером использования ПО промежуточного уровня и трехуровневой архитектуры является адаптер для виртуальной базы данных, который способен объединять данные из нескольких баз данных с различными схемами и моделями данных.