Страница 3 из 9 Создание приложения для просмотра метаданных
Итак, рассмотрим, как можно применить объекты ADOX в Delphi. В качестве иллюстрации создадим приложение, с помощью которого пользователь сможет: - просматривать метаданные в виде «дерева» объектов;
- изучать свойства объектов базы данных;
- получать исходный текст хранимых процедур и представлений.
Для выполнения этой задачи создадим новый проект и поместим на главную форму будущего приложения следующие компоненты: 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); ... В приведенном выше коде создается новая база данных — заранее заданного типа в заранее заданном каталоге. Затем мы можем добавить в эту базу данных таблицы и поля. Чтобы сделать это, необходимо выполнить такую последовательность действий: - Создать новый экземпляр объекта Table.
- Создать новый экземпляр объекта Column.
- Определить свойства вновь создаваемого поля.
- Добавить объект Column в коллекцию Columns объекта Table.
- Повторить шаги 3-4 для каждого нового поля.
- Добавить объект 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 будет реализована поддержка этих объектов для других типов баз данных. |