Borland Delphi и расширения ADO
Страница 3. Создание приложения для просмотра метаданных


 

Создание приложения для просмотра метаданных

Итак, рассмотрим, как можно применить объекты ADOX в Delphi. В качестве иллюстрации создадим приложение, с помощью которого пользователь сможет:

  1. просматривать метаданные в виде «дерева» объектов;
  2. изучать свойства объектов базы данных;
  3. получать исходный текст хранимых процедур и представлений.

Для выполнения этой задачи создадим новый проект и поместим на главную форму будущего приложения следующие компоненты: TMainMenu, TTreeView, TMemo и TStatusBar. В первую очередь нам следует включить ссылку на библиотеку типов ADOX (она находится в файле MSADOX.DLL), поскольку ADOX не поддерживается в Delphi 5 на уровне компонентов. Для этого следует выбрать Project | Import Type Library главного меню среды разработки Delphi, а затем из списка доступных библиотек типов выбрать Microsoft ADO Ext. 2.1 for DDL and Security. Чтобы избежать конфликтов с именами уже имеющихся классов Delphi (например, TTable), следует переименовать классы ADOX, заменив имена на что-нибудь типа TADOXxxx. Затем нужно убрать отметку из опции Generate Component Wrapper — в данном случае нам нужен только файл *.pas, содержащий интерфейс для доступа к объектам ADOX, а затем нажать кнопку Create Unit. Это приведет к созданию файла ADOX_TLB.PAS, содержащего интерфейс к библиотеке типов ADOX. Создав этот файл, мы должны сослаться на него, а также на модуль ADODB в предложении Uses главного модуля нашего проекта.

Теперь мы готовы к написанию кода для создания объектов ADOX. Создадим пункт меню File | Open Catalog и в обработчике его события OnClick напишем следующий код:

procedure TForm1.OpenCatalog1Click(Sender: TObject);
begin
// Получить имя источника данных (DataSource Name) с помощью стандартной
// диалоговой панели Microsoft
DS := PromptDataSource(Application.Handle, '');
// Если пользователь выбрал источник данных
If DS <> '' Then
   begin
// просмотреть метаданные
    BrowseData(DS);
   end;
end;

Здесь мы использовали метод PromptDataSource, реализованный в модуле ADODB, чтобы вызвать стандартную диалоговую панель Data Link Properties. Если источник данных выбран, то вызывается процедура BrowseData. Назначение этой процедуры — отобразить с помощью компонента TreeView метаданные, извлеченные из базы данных. Текст этой процедуры таков:

procedure TForm1.BrowseData(DataSource: String);
var
RootNode   : TTreeNode;
OneNode    : TTreeNode;
SubNode    : TTreeNode;
I             : Integer;
OldCursor : TCursor;
begin
// Заменить стандартный курсор на песочные часы
OldCursor := Screen.Cursor;
Screen.Cursor := crHourglass;
StatusBar1.Panels[0].Text := 'Extracting metadata, please wait...';
// Очистить компонент TreeView
ClearTree;
// и Memo
Memo1.Lines.Clear;
Application.ProcessMessages;
// Соединиться с источником данных
Catalog._Set_ActiveConnection(DataSource);
RootNode := TreeView1.Items.Add(Nil, 'Catalog');
// Добавить таблицы
OneNode   := TreeView1.Items.AddChild(RootNode, 'Tables');
For I := 0 to Catalog.Tables.Count-1 do
   begin
    SubNode := TreeView1.Items.AddChild(OneNode, Catalog.Tables[I].Name);
// Добавить поля, индексы и ключи
    ProceedTables(Catalog.Tables[I], SubNode);
   end;
// Добавить представления
If CheckViews(Catalog) then
begin
   OneNode   := TreeView1.Items.AddChild(RootNode, 'Views');
   For I := 0 to Catalog.Views.Count-1 do
    begin
      SubNode := TreeView1.Items.AddChild(OneNode, Catalog.Views[I].Name);
    end;
end;
// Добавить процедуры
OneNode   := TreeView1.Items.AddChild(RootNode, 'Procedures');
For I := 0 to Catalog.Procedures.Count-1 do
   begin
    SubNode := TreeView1.Items.AddChild(OneNode, Catalog.Procedures[I].Name);
   end;
RootNode.Expand(False);
// Заменить курсор на стандартный и очистить панель состояния
Screen.Cursor := OldCursor;
StatusBar1.Panels[0].Text := '';
end;

Обратите внимание на то, что в приведенной процедуре имеются три цикла для просмотра коллекций Tables, Views и Procedures объекта Catalog. Каждый найденный объект помещен в определенную ветвь компонента TreeView1. Коллекция Tables содержит один или более объектов Table, свойствами которых являются коллекции Columns, Indexes и Keys, и их также следует просмотреть. Это делается с помощью процедуры ProceedTables:

procedure TForm1.ProceedTables(T: Table; N : TTreeNode);
var
I          : Integer;
SubNode : TTreeNode;
begin
// Добавить поля
If T.Columns.Count > 0 Then
SubNode := TreeView1.Items.AddChild(N, 'Columns');
For I := 0 to T.Columns.Count-1 do
   TreeView1.Items.AddChild(SubNode, T.Columns.Item[I].Name);
// Добавить индексы
If T.Indexes.Count > 0 Then
SubNode := TreeView1.Items.AddChild(N, 'Indexes');
For I := 0 to T.Indexes.Count-1 do
   TreeView1.Items.AddChild(SubNode, T.Indexes.Item[I].Name);
// Добавить ключи
If T.Keys.Count > 0 Then
SubNode := TreeView1.Items.AddChild(N, 'Keys');
For I := 0 to T.Keys.Count-1 do
   TreeView1.Items.AddChild(SubNode, T.Keys.Item[I].Name);
end;

И снова код содержит три цикла для просмотра коллекций Columns, Indexes и Keys объекта Table .

Вернемся к процедуре BrowseData. Отметим, что перед циклом просмотра коллекции Views следует выполнить проверку того, доступны ли представления для текущего источника данных:

 If CheckViews(Catalog) then ...

Это делается, чтобы избежать ошибок и исключительных ситуаций, которые могут возникнуть, если ADOX не поддерживает коллекцию Views для текущего источника данных. Исходный текст функции CheckView показан ниже:

 function CheckViews(C : _Catalog) : Boolean;
var
I : Integer;
begin
try
   I := C.Views.Count;
   CheckViews := True;
except
   CheckViews := False;
end;
end;

Теперь компонент TreeView1 заполнен метаданными. Для получения информации о конкретном объекте следует создать для этого компонента обработчик события OnChange. Вот его текст:

 procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
If Node.Parent.Parent <> Nil Then
begin
   Case Node.Parent.Text[1] of
    'C' : ViewColumns(Node.Parent.Parent.Text, Node.Text);
    'I' : ViewIndexes(Node.Parent.Parent.Text, Node.Text);
    'K' : ViewKeys(Node.Parent.Parent.Text, Node.Text);
    'T' : ViewTables(Node.Text);
    'V' : ViewProps(Node.Text);
    'P' : ProcProps(Node.Text);
   end;
end;
end;

Как можно видеть, в этом обработчике события вызываются различные процедуры в зависимости от того, на какой из ветвей компонента TreeView1 пользователь щелкнул мышью. Например, процедура ViewTables показывает число объектов внутри выбранной таблицы, а процедуры ViewColumns, ViewIndexes и ViewKeys используются для изучения свойств полей, индексов и ключей.

Объекты Column, Index и Key обладают немалым количеством свойств, и в рамках данной статьи рассмотреть их подробно невозможно, поэтому в таблице 2 приведены их краткие описания.

Таблица 2

Column
Attributes Содержит характеристики поля
DefinedSize Содержит максимальный размер поля
NumericScale Содержит сведения о положении десятичной точки для числового поля
ParentCatalog Указывает на имя каталога, к которому принадлежит поле
Precision Содержит максимальную точность данных в поле
RelatedColumn Для ключевых полей содержит имя связанного поля
SortOrder Указывает порядок сортировки в данных для поля
Type Содержит тип данных, хранящихся в поле
Index
Clustered Указывает, является ли индекс кластерным
IndexNulls Указывает, как обрабатываются значения Null
PrimaryKey Указывает, реализует ли данный индекс первичный ключ
Unique Указывает, должен ли быть уникальным ключ, реализованный в данном индексе
Key
DeleteRule Указывает, каким образом обрабатывается удаление записи, содержащей первичный ключ
RelatedTable Для внешнего ключа указывает имя связанной таблицы
Type Содержит тип ключа
UpdateRule Указывает, как производится обновление записи, содержащей первичный ключ

Процедуры ViewProps и ProcProps предназначены для вывода исходного текста представлений и хранимых процедур. Рассмотрим, например, процедуру ProcProps, отображающую свойства хранимой процедуры:

 procedure TForm1.ProcProps(Name : String);
var
S          : String;
Disp      : IDispatch;
Command : _Command;
begin
S := 'PROCEDURE : ' + Catalog.Procedures.Item[Name].Name;
S := S + ^M^J + 'Created    : ' +
   VarToStr(Catalog.Procedures.Item[Name].DateCreated);
S := S + ^M^J + 'Modified   : ' +
   VarToStr(Catalog.Procedures.Item[Name].DateModified);
If CmdSupported(Catalog.Procedures.Item[Name]) Then
begin
   Disp := Catalog.Procedures.Item[Name].Get_Command;
   Command := Disp AS Command;
   S := S + ^M^J^M^J + Command.Get_CommandText;
end;
Memo1.Text := S;
end;

В вышеописанном коде мы использовали тот факт, что член коллекции Procedures в действительности указывает на объект ADO Command. Следовательно, мы можем использовать метод Get_Command для получения интерфейса IDispatch объекта Command и использовать их метод Get_CommandText для получения исходного текста хранимой процедуры.

Теперь мы знаем, как использовать объекты ADOX для извлечения и отображения метаданных. Еще одна возможность ADOX, которая будет кратко рассмотрена ниже, — создание баз данных и объектов внутри них без применения сложных DDL-запросов.

Создание баз данных и их объектов

Первый шаг при создании новой базы данных — создание нового экземпляра объекта Catalog. Это позволяет определить не только тип создаваемой базы данных (с помощью OLE DB-провайдера), но и местоположение файла базы данных. Вот как это можно сделать для базы данных Microsoft Access:

 const
BaseName =   'c:\data\demo.mdb';
DS          =   'Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+BaseName;
var
Catalog   : TADOXCatalog;
...
// Создать экземпляр объекта ADOX Catalog
Catalog   := CoCatalog.Create;
// Если база данных существует, удалим ее
If FileExists(BaseName) Then DeleteFile(BaseName);
// Создадим новый MDB-файл
Catalog.Create(DS);
// Укажем активное соединение
Catalog._Set_ActiveConnection(DS);
...

В приведенном выше коде создается новая база данных — заранее заданного типа в заранее заданном каталоге. Затем мы можем добавить в эту базу данных таблицы и поля. Чтобы сделать это, необходимо выполнить такую последовательность действий:

  1. Создать новый экземпляр объекта Table.
  2. Создать новый экземпляр объекта Column.
  3. Определить свойства вновь создаваемого поля.
  4. Добавить объект Column в коллекцию Columns объекта Table.
  5. Повторить шаги 3-4 для каждого нового поля.
  6. Добавить объект Table в коллекцию Tables объекта Catalog.

Следующий пример показывает, как можно реализовать эту последовательность действий:

 // ШАГ 1
// Создать новый экземпляр объекта Table
Table             := CoTable.Create;
// Give it the name
Table.Name      := 'Customers';
// И определить Catalog, к которому он принадлежит
Table.ParentCatalog := Catalog;
// ШАГ 2
// Создать новый экземпляр объекта Column
Column := CoColumn.Create;
With Column do
   begin
    ParentCatalog := Catalog;
// ШАГ 3
// Установить свойства
    Name    := 'CustID';
    Type_   := adInteger;
    Properties['Autoincrement'].Value := True;
    Properties['Description'].Value    := 'Customer ID';
   end;
// ШАГ 4
// Добавить поле к коллекции Columns объекта Table
   Table.Columns.Append(Column, 0, 0);
   Column := Nil;
// ШАГ 5
// Создать несколько объектов Columns и добавить их к объекту Table
   With Table.Columns do
    begin
      Append('FirstName', adVarWChar, 64);
      Append('LastName',   adVarWChar, 64);
      Append('Phone',       adVarWChar, 64);
      Append('Notes',       adLongVarWChar, 128);
    end;
// ШАГ 6
// Добавить объект Table в коллекцию Tables объекта Catalog
Catalog.Tables.Append(Table);
Catalog := Nil;

После того как таблица создана и ее поля определены, мы можем добавить индексы и ключи, если в том есть необходимость. Следующий пример показывает, как добавить индекс к LastName имеющейся таблицы:

 Index := CoIndex.Create;
With Index do
   begin
    Name := 'LastNameIndex';
    IndexNulls := adIndexNullsDisallow;
    Columns.Append('LastName', adVarWChar, 64);
    Columns['LastName'].SortOrder := adSortAscending;
   end;
   Table.Indexes.Append(Index, EmptyParam);

Логика этого примера проста: сначала мы создаем экземпляр объекта Index, затем устанавливаем его свойства — имя, способ обработки «пустых» значений, связываем индекс с полем и, наконец, добавляем его в коллекцию Indexes объекта Table . Примерно то же самое следует делать и при создании ключей.

Обсудив объекты ADOX, мы не коснулись объектов User и Group . В настоящее время в текущей версии ADO эти объекты доступны только для Microsoft Access (Microsoft Jet OLE DB Provider), и пока нет никаких сведений о том, что в последующих версиях ADO будет реализована поддержка этих объектов для других типов баз данных.

 
« Предыдущая статья   Следующая статья »