ADOX.CATALOG的东东

来源:互联网 发布:spf算法是什么意思 编辑:程序博客网 时间:2024/05/22 02:28

基于ADOX的数据库管理(ADOX、JRO)

作者:webfly 日期:2005-04-06


众所周知,Delphi最主要的特性之一就是可以用来开发数据库程序。但是在Delphi 5以前数据库的开发主要是基于BDE(Borland数据库引擎)的,而BDE存在很多问题,包括过于庞大、不易分发、不稳定等。因此,Borland决定今后将放弃BDE的进一步开发,当前BDE已经处于维护阶段,只是修正Bug而已。

但是BDE的停止更新并不意味着Borland停止技术革新的脚步了,Delphi 5中包括了一个很重要的新特性,那就是可以用ADO Express架构来开发数据库程序。ADO Express控件提供了对微软的ActiveX Data Objects (ADO)的封装(ADO属于微软通用数据存取架构的一部分),它给我们提供了BDE以外的数据库编程的强大功能,随着微软对ADO功能的改进,它正在变得越来越强大,除此之外微软还提供了ADO的扩展来进一步拓展它的应用。

    接下来我们就来研究一下ADO的扩展,本文的第一部分主要是关于DDL(数据定义语言)、安全机制( ADOX)和Jet及Replication对象库 (JRO);第二部分我们则要来研究一下多维ADO (ADO MD)。

ADOX的介绍

    ADOX 可以用来执行一系列用ADO无法单独实现的功能。例如,使用ADO我们要想对已有的数据库的结构进行修改是非常麻烦的,但使用ADOX就可以很容易地做到,同时ADOX还提供了提取数据库的用户信息或创建新的用户账号等方面的扩展。ADOX扩展了ADO的对象模型,提供了10个新的对象,它们都可以和ADO配合使用。比如我们可以用ADO Connection对象连接到一个数据源,并提取出元数据(注意:元数据就是对数据库结构的描述,包括表、字段、索引、关键字段、存储过程的定义等,而不是指数据库包含的数据内容),大多数数据库使用SQL来定义元数据。在ADOX出现以前,提取元数据的唯一的方法是使用ADO Connection对象的OpenSchema方法,而且要想创建新的数据库对象,我们还只能使用基于SQL的DDL对象和ADO Command对象。 

    ADOX提供了一种不需要懂SQL就可以操纵元数据的方式。但要注意的是 ADOX并不支持所有的数据库,它只局限于微软的Access, SQL Server, 以及其他几种数据库。要想详细了解这方面的信息,请参看http://www.microsoft.com/data。 

    ADOX的对象模型如图1.81所示,图1.82显示了表的对象模型。ADOX对象表的内容如表1.5所示,ADOX的最上层对象是Catalog。它的下级对象包括表、视图、过程、用户以及账号。Catalog对象可以用来打开数据库 (通过ADO Connection 对象),或创建一个新的数据库。到ADO 2.1版本为止,我们还只能创建Jet 4.0数据库,在未来版本中这一功能有可能扩展到其他数据库。

   

图1.81                                         图1.82

表1.5  ADOX对象的简单描述

对    象

                       说    明

Catalog

包含描述数据源模式目录的集合,提供对表、视图、用户、过程和账号的操作

Column

表示表、索引或关键字的列

Group

表示在安全数据库内具有访问权限的账号

Index

表示数据库表的索引

Key

表示数据库表中的主关键字、外部关键字或唯一关键字字段

Procedure

表示存储过程或查询

Table

表示包括列、索引和关键字的数据库表

User

表示在安全数据库中具有访问权限的用户账号

View

表示记录或虚拟表的过滤集

    我们可以使用Catalog对象对表、过程以及视图进行操作。比如,通过枚举表集合,可以知道当前数据库有哪些表。更进一步,可以获得一个表的字段、索引、关键字段等元数据信息。通过用户和账号对象集合,可以获得数据库的安全信息(注意:这个功能只对安全数据库有效,对于Access数据库,我们必须在数据源连接字符串中包括System.mdw )。

    ADOX另一个强大的功能是可以用Catalog 对象来创建新的数据库,并通过表、字段、索引对象的Add方法向数据库添加表、字段、索引和关键字段。 

创建一个简单的ADOX察看器

    接下来就演示一下如何在Delphi中使用ADOX。我们将创建一个应用程序,主要功能是:

    (1)显示数据库的元数据。

    (2)显示数据库对象的属性。

    (3)显示视图和存储过程的源代码。

图1.83

    首先创建一个新的项目,在主窗体上放置下列控件: MainMenu、TreeView、Memo和 StatusBar。完成的程序示意如图1.83所示。

    接下来,我们要引入ADOX的类型库。先选择菜单项 Project | Import Type Library,然后从类型库列表中选择Microsoft ADO Ext. 2.1 for DDL and Security。为了避免同VCL已有控件名冲突,对ADOX类要重新命名(比如TTable可以改成TADOXTable),按Create Unit按钮来生成接口文件。Delphi会生成ADOX_TLB.pas文件,我们需要在项目的Uses部分引用它。 

    现在创建一个File | Open Catalog菜单项,并生成一个OnClick事件处理函数,代码如下: 

    procedure TForm1.OpenCatalog1Click(Sender: TObject);

    begin

图1.84

      // 通过微软提供的对话框来获得数据源

      DS :=PromptDataSource(Application.Handle,'');

      //如果用户选择了一个数据源.

      if DS <> '' then

        BrowseData(DS);

    end;

    这里使用了在ADODB单元中提供的PromptDataSource方法来显示标准的数据连接属性对话框(如图1.84)。

    注意:这里应该选择Microsoft Jet 4.0 OLE DB Provider作为数据源,连接到Access的样例数据库Northwind.mdb,然后程序会调用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 :=

        '提取源数据,请等待';

      // 清空TreeView和Memo

      ClearTree;

      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

          SubNode := TreeView1.Items.AddChild(

                  OneNode, Catalog.Views[I].Name);

      end;

      //添加过程信息

      OneNode := TreeView1.Items.AddChild(RootNode,

                 'Procedures');

      for I := 0 to Catalog.Procedures.Count-1 do

        SubNode := TreeView1.Items.AddChild(

                OneNode, Catalog.Procedures[I].Name);

        RootNode.Expand(False);

        //恢复缺省光标清空状态条

        Screen.Cursor := OldCursor;

        StatusBar1.Panels[0].Text := '';

    end;

    通过三个循环我们遍历了所有的表、视图对象。每个对象都被放到了TreeView中的适当分支上。此外,每个表还都需要处理对应的字段、索引和关键字段列表,这里是通过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;

    现在回到BrowseData过程,会看到在循环视图对象前,执行了如下校验过程:

    if CheckViews(Catalog) then ...

    这是为了避免不支持视图的数据源可能带来的错误,CheckView函数代码如下: 

    function CheckViews(C: _Catalog): Boolean;

    var

      I : Integer;

    begin

      try

        I := C.Views.Count;

        CheckViews := True;

        except

        CheckViews := False;

      end;

    end;

    为了获得更详细的对象信息,可通过实现TreeView控件的OnChange事件来显示元数据,代码如下: 

    procedure TForm1.TreeView1Change(Sender: TObject;

      Node: TTreeNode);

    begin

      if Node.Parent.Parent <> nil then

        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;

    过程通过调用ViewColumns、ViewIndexs、ViewKeys来显示被选对象的属性,表1.6~表1.8是相应对象的详细信息。

表1.6  字段对象属性

属    性

                说    明

Attributes

描述列特性

DefinedSize

指示列的规定最大尺寸

NumericScale

指示列中数值的范围

ParentCatalog

指定表或列的父目录以便访问特定提供者的属性

Precision

指示列中数值的最高精度

RelatedColumn

指示相关表中相关列的名称(仅关键字列)

SortOrder

指示列的排序顺序(仅索引列)

Type

字段类型

表1.7  索引对象属性

属    性

                说    明

Clustered

指示索引是否被分簇

IndexNulls

指示在索引字段中具有 Null 值的记录是否有索引项

PrimaryKey

指示索引是否代表表的主关键字

Unique

指示索引关键字是否必须是唯一的

表1.8  关键字段属性

属    性

                说    明

DeleteRule

指示主关键字被删除时将执行的操作

RelatedTable

指示相关表的名称

Type

指示列的数据类型

UpdateRule

指示主关键字被更新时会执行的操作

    ViewProps和ProcProps过程则是用来显示视图和存储过程的。其中ProcProps代码如下,ViewProps 过程与此类似:

    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;

    其中存储过程可以通过ADO Command 对象获得,我们先使用Get_Command方法来获得Command对象的IDispatch接口,然后调用接口的Get_CommandText方法来获得存储过程的源代码。

    在了解了如何使用ADOX来获得数据源的元数据后,接下来要研究的就是如何使用ADOX来不使用复杂的SQL DDL语句就可以创建数据库。

创建数据库

    首先需要创建Catalog对象的一个实例。通过CataLog对象不仅可以指定要创建的数据库的类型,还可以指定数据库文件的位置。代码如下:

    const

      BaseName = 'c:\data\demo.mdb';

      DS = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' +

           BaseName;

    var

      Catalog : TADOXCatalog;

      ...

      //创建Catalog对象的一个实例

      Catalog := CoCatalog.Create;

      //如果数据库已经存在,就删除它

      if FileExists(BaseName) then

        DeleteFile(BaseName);

      //创建新的.mdb文件

      Catalog.Create(DS);

      //指定活动连接

      Catalog._Set_ActiveConnection(DS);

      ...

    创建好数据库后,我们要向库中添加表和字段,这包括下面几个步骤:

    (1)创建表对象的一个实例。

    (2)创建字段对象的一个实例。

    (3)设定新字段的属性。

    (4)继续添加新的字段

    (5)重复第(3)、(4)步。

    (6)添加新的表。

    代码如下

    //第1步创建表对象的一个实例

    Table := CoTable.Create;

    //指定表名

     Table.Name := 'Customers';

    // ...设定表所属的Catalog

     Table.ParentCatalog := Catalog;

    //第2步创建字段对象的一个实例

    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步添加新的字段

    Table.Columns.Append(Column, 0, 0);

    Column := nil;

    //第5步重复第3、4步

    with Table.Columns do begin

      Append('FirstName', adVarWChar, 64);

      Append('LastName', adVarWChar, 64);

      Append('Phone', adVarWChar, 64);

      Append('Notes', adLongVarWChar, 128);

    end;

    //第6步添加新的表

    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对象,然后设定它的Name属性,指定如何处理NULL索引,将其同字段相关联,最后添加到表中,关键字段实现原理与此类似。

    到目前为止,我们还没有提到如何使用用户和账号对象,这是由于当前的项目是基于Access数据库的,而这两个对象是Access不支持的。

使用Jet 和Replication对象

    另一个ADO的扩展是Jet和Replication对象 (JRO)。ADOX能对不同的数据源操作,而JRO只对Jet数据库起作用。也就是只能操作Access数据库。 

    JRO提供了一组对象,可以创建、修改和同步复制数据库。其核心对象是Replica,它可以用来创建新的Replica,获得Replica的属性,根据其他Replica的改变来同步更新。

图1.85

    JRO框架还包括有JetEngine对象,JetEngine对象提供了一些Jet引擎的特性,特别是能够压缩数据库、设定数据库密码、对数据库加密和从内存缓冲中刷新数据。对象模型如图1.85所示。

    这里只简单介绍一下用JRO来复制数据库。

    第一步,要创建一个design master(设计原型),表明数据库将用来作为一个复制的数据源,可以用来复制。首先需要调用Replica对象的MakeReplicable方法,然后可以用GetObjectReplicability和SetObjectReplicability方法来改变数据库的replicability状态。根据情况可以创建部分或者完全的设计原版的复本。

    第二步,可以利用Filter对象来定义更新规则。最后,可以在两个复本之间保持同步,可以在Internet上进行直接或间接的同步。如果是间接同步,需要使用微软公司的Office Developer带的复制管理器。 

    注意:要想在Delphi中使用JRO库,需要引入Microsoft Jet and Replication Objects 2.1 Library (2.1版本)的类型库。 

使用JetEngine对象

    下面代码显示了如何使用JetEngine对象来压缩Northwind.mdb数据库,并创建一个新的压缩后的Northwind.mdb数据库的复本NewNorth.mdb:

    const

      Provider = 'Provider=Microsoft.Jet.OLEDB.4.0;';

       //替换下面的路径为微软Access例子库的真实路径

      SrcMDB = 'c:\data\northwind.mdb';

      DstMDB = 'd:\data\newnorth.mdb';

    procedure TForm1.Button1Click(Sender: TObject);

    var

      JetEng : JetEngine;

      Src : WideString;

      Dest : WideString;

    begin

,

      //创建JetEngine对象

      JetEng := CoJetEngine.Create;

      //设定数据源

      Src := Provider + 'Data Source=' + SrcMDB;

      //设定目的源

      Dest := Provider + 'Data Source=' + DstMDB;

      //如果目标数据库存在,就删除它

      if FileExists(DstMDB) then

        DeleteFile(DstMDB);

        //压缩数据库

        JetEng.CompactDatabase(Src, Dest);

        //释放JetEngine对象

        JetEng := nil;

    end;

    让我们稍微深入地解释一下压缩数据库的过程。表的页面首先被重组。压缩后,原来碎片的数据页面被放在相邻的数据库页面中,这可以极大地改进数据库的性能。通过删除原来只是被标记为删除的记录来回收未被使用的空间。然后自增加字段被重置,以便下一个分配的值可以保持连续。最后更新用来优化查询的表的统计信息,这是因为统计信息被改变了,所有的查询都会被标记,下一次运行查询时,查询又会被重新编译。

结论

    本文介绍了两个ADO扩展:DDL和Security (ADOX)以及Jet和Replication对象 (JRO)。我们还简单研究了一下如何使用ADOX对象来获得数据源的元数据。如何创建新数据库和如何使用JRO来压缩Jet数据库,并简单介绍了数据库复制过程。

    第二部分我们将要介绍ADO Multi-Dimensional (ADO MD),它可以用来操作多维数据存储。



ADOX.CATALOG的东东

299人阅读 评论(0)收藏 举报

自己在搞老钟的软件时候很有想法,把数据库也要区域化,所以试着做所谓的“翻译”,目前整了ACCESS的,还不知道好用不。

实际上就是同时有两套资源,一套用于捕捉识别→定位,一套用于更新,当然,两套内容被放到了顺序一一对应的两个数组里。

主要的工作是,更改ACCESS里的表名、表中的列名,最后更改文件名。

要查到现有数据库的表名才能和第一套资源进行比对,所以

开始的时候想,能不能在TSQL里写查询,网上也给出的可行性,就是像SQL SERVER一样查询系统表。可是,ACCESS的系统表有权限限制,没弄明白怎么在代码里改权限(估计即便弄明白了,也要引用ACCESS的某个模块,搞一堆复杂的方法),所以就放弃了。

后来看到可以用ADOX直接修改,高兴了。

可是不知道怎么用CATALOG连接数据库,试着用连接字符串、OLEDBCONNECTION和DBCONNECTION向CATALOG.ACTIVECONNECTION赋值,都报错,不高兴了。

最后看到说,ACTIVECONNECTION只能接受已经打开的ADODB.CONNECTION,虽然觉得不好,但也只能这样。

真正的问题:

在CATALOG对表名修改后,并没有即时的在硬盘上生效,而CATALOG也没有提供更新的功能。这使得每次操作后,若需要上一次操作结果,就必须彻底释放上一个CATALOG或重新实例化新的CATALOG。

在一个试验用的小程序里,不给出ADODB.CONNECTION,而使用CATALOG.CREATE(connectionstring),我发现即便让CATALOG=NULL也不能解除数据库的锁定状态,也就是说,连接仍然开着;而提供了CONNECTION的情形能够正常的解锁(connection.CLOSE())。

这似乎造成了一个硌硬人的局面:在创建.mdb文件后,连接要保持打开到程序结束!

其实回头想一想,就会知道,ACTIVECONNECTION属性就是连接所在的位置,只要将这个连接关闭就可以了,当然,要关闭这个连接,就不能只引用ADOX,还必须引用ADO,至于是强制转换还是什么的,那就好说了。

这个试验使我明白了:ADO和ADOX是天造地设的一对,任何试图拆散他们的想法终会遇到重重困难~


原创粉丝点击