通过 DB2 表为 Delphi 产生动态的数据输入窗体

本文检查了 IBM DB2 Universal Database 的元数据,以便动态构建视图和窗体,其中包括如何动态生成新的 CLX 窗体,以及将窗体流化(stream)为 Delphi .pas 和 .xfm 文件,并准备将它们添加到 Kylix 和 Delphi CLX 项目中。

简介
在 上一篇文章中,我检查了 IBM® DB2® Universal Database™(UDB)的元数据,以便动态构建视图和窗体。我使用了 Linux 上的 Borland® Kylix™ 3 和 dbExpress 数据访问驱动程序,来分析 IBM DB2 UDB 数据库表、字段(名称和类型),允许用户选择特定的表,来回切换应该显示的字段,以及在数据网格(datagrid)和单个数据感知控件中动态查看输出。

这次,我们将扩展该方法,以允许用户指定更加复杂的查询(例如 JOIN)。作为附加步骤,我们将产生一个单独的窗体,并以 .pas 和 .xfm 源格式保存它,因此,可以将结果窗体添加至 Borland Delphi™ 或 Kylix 项目中。这类似于原来的基于 BDE(且仅用于 Windows)的数据库-窗体向导,但这次产生的是一个跨平台的 CLX 窗体。

从 Linux 进行迁移
上次,我完成了一个在 Linux 上运行的 Kylix 3 项目。这次,我首先需要将该项目迁移到 Windows 上,并在那里进行扩展(用以说明该项目和由此生成的 CLX 窗体是跨平台的,既可以用于 Linux 上的 Kylix,也可用于 Windows 上的 Delphi)。

将该项目从 Kylix 迁移到 Delphi 仅涉及一项更改:LibraryName 和 VendorLib 的 TSQLConnection 属性在 Linux 上被设置为 libsqldb2.so 和 libdb2.so,但在 Windows 上,该属性必须设为 dbexpdb2.dll 和 db2cli.dll。关于如何创建跨平台项目的指导,请参阅 大转变:利用 Kylix 3 从 Windows 迁移到 Linux。

添加 SQL 功能
一旦可以在 Delphi 7 中重新打开该项目,您就可以对页面控制的 Meta Data 选项卡进行一些增强。具体地说,您需要允许用户输入 SQL 语句,而不是只能从可用表的列表中选择表名。图 1 展示了新的用户界面(显示了 EMP_PHOTO 表的字段),您可以使用左下方的 memo 字段输入 SQL 语句。

图 1. 用于 DB2 的新动态窗体
图 1. 用于 DB2 的新动态窗体

就在 TMemo 控件的正上方,我放置了文本 SELECT * FROM,作为小小的一个提示:用户只需指定表名,以及下面可选的 where子句。可以用代码来构造 SQL 语句,检索元数据(metadata)来获取字段名,并将这些字段名置于 TCheckListBox 中,该代码如下所示:  

procedure TDBWizForm.QCheckBoxClick(Sender: TObject);

var i: Integer;

begin

   if QCheckBox.Checked then begin ClientDataSet1.Active := False;   

      SQLDataSet1.CommandType := ctQuery;

      SQLDataSet1.CommandText := 'select * from ' + QMemo.Text;

      SQLDataSet1.FieldDefs.Update; // get meta information 

      CheckListBox1.Clear;

      for i:=0 to Pred(SQLDataSet1.FieldDefs.Count) do

        CheckListBox1.Items.Add(SQLDataSet1.FieldDefs[i].Name)

      end

end;

作为一个实际示例,我输入了一条 SQL 语句,以连接 EMPLOYEE 和 EMP_PHOTO 表,该操作是基于 EMPNO 字段的。因为已经给定了 SELECT * FROM,所以我只需要在查询 memo 字段中输入 EMP_PHOTO, EMPLOYEE WHERE EMP_PHOTO.EMPNO = EMPLOYEE.EMPNO,然后启用 Select fields from query复选框,这将执行以上代码,产生 TCheckListBox 中的字段列表。

图 2. 从查询中选择字段
图 2. 从查询中选择字段

请注意,EMPNO 字段出现了两次:一次是在 EMP_PHOTO 表中(称作 EMPNO),一次是在 EMPLOYEE 表中(然后自动称作 EMPNO_1)。

在上面的实例中,通过左下方所指定的 where子句,我从查询中选择了 PICTURE、FIRSTNME、LASTNAME、PHONENO、HIREDATE 和 SEX 字段。

为了给 DBGrid 和其他数据感知控件提供真正的数据,您需要添加一些代码,以获取表名(从 TListBox 中)或 where子句(从 TMemo 控件中)。这项决策是基于复选框的启用情况的,其编码如下:

SQLDataSet1.CommandText := 'SELECT ';

comma := False;

for i:=0 to Pred(CheckListBox1.Items.Count) do

begin

   if CheckListBox1.Checked[i] then

   begin

     if not comma then comma := True

     else    

     SQLDataSet1.CommandText :=SQLDataSet1.CommandText + ', ';

     SQLDataSet1.CommandText := SQLDataSet1.CommandText + CheckListBox1.Items[i]

   end

end;

   if QCheckBox.Checked then

   SQLDataSet1.CommandText := SQLDataSet1.CommandText + ' FROM ' + QMemo.Text

   else if

     ListBox1.ItemIndex >= 0 then SQLDataSet1.CommandText := SQLDataSet1.CommandText

     + ' FROM ' + ListBox1.Items[ListBox1.ItemIndex];

这确保您可以使用带有表名的 TListBox,或使用带有 SQL where 子句的 TMemo ,来选择字段并为视图产生数据。

添加更多的控件
您在图 2 中选择的字段是 PICTURE 字段。上次,我在由 TDBMemo 表示的 Memo 字段之间进行了区分,虽然其他所有字段都是由 TDBEdit 表示的。对于可以是 ftGraphic 或 ftBlob(本实例中)的 image 字段,您可以使用 TDBImage 控件。请注意,ftBlob 的情况并不是百分之百确定的:大多数 BLOB 字段只包含二进制数据,而不是总包含图像。但是对于本例,ftBlob PICTURE 字段包含图像(以不同的格式,如 bmp 或 gif)。

必须添加一些附加代码,以检测字段类型,并通过动态创建 TDBImage 对此作出响应,这些附加代码片断如下所示:

if (ClientDataSet1.FieldDefs[i].DataType = ftGraphic) or (ClientDataSet1.FieldDefs[i].DataType = ftBlob) then

begin

  with TDBImage.Create(Self) do

   begin

    Parent := TabSheet3;

    Left := 126;

    Top := Y - 4;

    Y := Y + 204;

    Height := 200;

    Width := 200;

    DataSource := DataSource1;

    DataField := ClientDataSet1.FieldDefs[i].Name;

   end

end ;

在您选择了 PICTURE、FIRSTNME、LASTNAME、PHONENO、HIREDATE 和 SEX 字段的示例中,该代码的结果如图 3 所示。

图 3. 动态数据控件预览
图 3. 动态数据控件预览

请注意,这里还有一个附加的按钮,在这里,将用它在新的 CLX 窗体上从该 tabsheet 重新创建这些控件。该窗体将被流化为 Delphi .pas 源文件和 .xfm 窗体文件;并准备将它们添加至 Kylix 或 Delphi CLX 项目中。

设计新的窗体框架
虽然在 tabsheet 中查看数据效果极佳(如图 3 中),但是在新的窗体(不同于原先那个您指定了表名或查询,并选择了要使用的字段名的窗体)中查看控件将具有更强大的功能。您必须重新创建 TLabel、TDBEdit、TDBMemo 和 TDBImage 控件,这意味着您可以从一个几乎为空的框架窗体开始,准备创建到 DB2 UDB 数据库新的 dbExpress 连接,输入 SQL 语句,并放置数据感知控件,以显示数据。

通过 Delphi 7,我设计了一个新的 CLX 窗体,该窗体包含 TSQLConnection、TSQLDataSet、TDataSetProvider、TClientDataSet、TdataSource 和 TDBNavigator 控件;其布局如图 4 中所示。像您以前多次做过的那样,将它们都关联起来,但是此时还没有任何数据感知控件。

图 4. 设计时的新窗体模板
图 4. 设计时的新窗体模板

现在,应该为图 3 中首次出现的 Generate Code 按钮实现 OnClick 事件处理程序了。

生成新的窗体内容
Generate Code 按钮将创建新 CLX 框架窗体的一个实例,并动态添加 TLabel、TDBEdit、TDBMemo 和 TDBImage 控件(使用与原来窗体相同的属性值)。用来创建新的 CLX 窗体并克隆将放置在新 CLX 窗体上的控件的代码如下所示:

procedure TDBWizForm.btnWizardClick(Sender: TObject);

var

ThisForm: TDBWizForm;

NewForm: TNewForm;

LabelNr,EditNr,MemoNr,ImageNr,i: Integer;

begin

  ThisForm := Self;

  NewForm := TNewForm.Create(Self);

try

   NewForm.Caption := 'Dr.Bob's Database Form Wizard';  

   NewForm.SQLConnection1.Params.Clear;

   NewForm.SQLConnection1.Params.Assign(ThisForm.SQLConnection1.Params);

   NewForm.SQLDataSet1.CommandText := ThisForm.SQLDataSet1.CommandText;

   LabelNr := 0;

   EditNr := 0;

   MemoNr := 0;

   ImageNr := 0;

  for i:=0 to Pred(ThisForm.ComponentCount) do

  begin

    if (ThisForm.Components[i] is TLabel) then

      begin

        with TLabel.Create(NewForm) do

         begin

           Inc(LabelNr);

           Name := Format('Label%d',[LabelNr]);

           Parent := NewForm;

           Left := (ThisForm.Components[i] as TLabel).Left;

           Top := (ThisForm.Components[i] as TLabel).Top;

           Width := (ThisForm.Components[i] as TLabel).Width;

           Alignment := taRightJustify;

           Caption := (ThisForm.Components[i] as TLabel).Caption

         end

      end

      else if (Components[i] is TDBEdit) then

      begin

        with TDBEdit.Create(NewForm) do

        begin Inc(EditNr);

         Name := Format('Edit%d',[EditNr]);

         Parent := NewForm;

         Left := (ThisForm.Components[i] as TDBEdit).Left;

         Top := (ThisForm.Components[i] as TDBEdit).Top;

         Width := 200;

         DataSource := NewForm.DataSource1;

         DataField := (ThisForm.Components[i] as TDBEdit).DataField

        end

      end

      else if (Components[i] is TDBMemo) then

      begin

       with TDBMemo.Create(NewForm) do

       begin 

         Inc(MemoNr);

         Name := Format('Memo%d',[MemoNr]);

         Parent := NewForm;

         Left := (ThisForm.Components[i] as TDBMemo).Left;

         Top := (ThisForm.Components[i] as TDBMemo).Top;

         Height := 200;

         Width := 200;

         DataSource := NewForm.DataSource1;

         DataField := (ThisForm.Components[i] as TDBMemo).DataField

        end

     end

    else if (Components[i] is TDBImage) then

    begin

     with TDBImage.Create(NewForm) do

     begin Inc(ImageNr);

      Name := Format('Image%d',[ImageNr]);

      Parent := NewForm;

      Left := (ThisForm.Components[i] as TDBImage).Left;

      Top := (ThisForm.Components[i] as TDBImage).Top;

      Height := 200;

      Width := 200;

      DataSource := NewForm.DataSource1;

      DataField := (ThisForm.Components[i] as TDBImage).DataField

     end

   end

end;

try

  NewForm.ClientDataSet1.Active := True;

except

end;

NewForm.ShowModal; // See Figure 5. NewForm.ClientDataSet1.Active := False; NewForm.SQLConnection1.Connected := False; WriteComponentResFile(UnitName+'.xfm', NewForm);

finally NewForm.Free

end

end;

在该事件处理程序的结尾,通过调用 ShowModal 显示了 NewForm,其结果如图 5 所示。因为就在显示该窗体之前激活了 TClientDataSet(关闭该窗体之时再取消激活状态),所以该图将显示带有数据的新窗体。

图 5. 运行时的新窗体
图 5. 运行时的新窗体

在显示了该窗体之后,您可以创建一个 Delphi .xfm 文件,其中包含窗体的流化(streaming)信息、其上所有的组件及其属性信息。对 WriteComponentResFile 的调用将为该窗体创建一个二进制的源文件。该文件可用于 Kylix 或 Delphi CLX 项目,但是还需要相应的 .pas 文件,其中包含窗体上每个组件的源代码声明。

生成相关的源代码
最后一步(也集成在“Generate Code”OnClick 事件处理程序中)是创建包含组件声明的相应的 .pas 文件。这是一个由三个阶段组成的过程。首先,您要编写该 .pas 文件的第一部分,然后动态添加单个 TLabel、TDBEdit、TDBMemo 和 TDBImage 控件,最后,添加该 .pas 文件的最后一部分。

procedure TDBWizForm.btnWizardClick(Sender: TObject); var f: System.Text; LabelNr,EditNr,MemoNr,ImageNr,i: Integer; begin System.Assign(f,UnitName+'.pas'); Rewrite(f); writeln(f,'unit ',UnitName,';'); writeln(f,'interface'); writeln(f,'uses'); writeln(f,' SysUtils, Types, Classes, Variants, QTypes, QGraphics, QControls, QForms,'); writeln(f,' QDialogs, QStdCtrls, DBXpress, QCheckLst, DB, SqlExpr, QComCtrls, FMTBcd,'); writeln(f,' DBClient, Provider, QGrids, QDBGrids, QDBCtrls, QExtCtrls, QMask;'); writeln(f); writeln(f,'type'); writeln(f,' TNewForm = class(TForm)'); writeln(f,' SQLConnection1: TSQLConnection;'); writeln(f,' SQLDataSet1: TSQLDataSet;'); writeln(f,' DataSetProvider1: TDataSetProvider;'); writeln(f,' ClientDataSet1: TClientDataSet;'); writeln(f,' DataSource1: TDataSource;'); writeln(f,' DBNavigator1: TDBNavigator;'); LabelNr := 0; EditNr := 0; MemoNr := 0; ImageNr := 0; for i:=0 to Pred(ThisForm.ComponentCount) do begin if (ThisForm.Components[i] is TLabel) then begin Inc(LabelNr); Name := Format('Label%d',[LabelNr]); writeln(f,' ',Name,': TLabel;'); end else if (Components[i] is TDBEdit) then begin Inc(EditNr); Name := Format('Edit%d',[EditNr]); writeln(f,' ',Name,': TDBEdit;'); end else if (Components[i] is TDBMemo) then begin Inc(MemoNr); Name := Format('Memo%d',[MemoNr]); writeln(f,' ',Name,': TDBMemo;'); end else if (Components[i] is TDBImage) then begin Inc(ImageNr); Name := Format('Image%d',[ImageNr]); writeln(f,' ',Name,': TDBImage;'); end end; writeln(f,' procedure FormCreate(Sender: TObject);'); writeln(f,' private'); writeln(f,' { Private declarations }'); writeln(f,' public'); writeln(f,' { Public declarations }'); writeln(f,' end;'); writeln(f); writeln(f,'var'); writeln(f,' NewForm1: TNewForm;'); writeln(f); writeln(f,'implementation'); writeln(f); writeln(f,'{$R *.xfm}'); writeln(f); writeln(f,'procedure TNewForm.FormCreate(Sender: TObject);'); writeln(f,'begin'); writeln(f,' try'); writeln(f,' ClientDataSet1.Active := True'); writeln(f,' except'); writeln(f,' end'); writeln(f,'end;'); writeln(f); writeln(f,'end.'); System.Close(f); end;

完整的源代码集成位于事件处理程序之中,可通过下载获得。

使用结果
其结果就是一个 .pas 以及相应的 .xfm 文件,可以将它们用于 Linux 上的 Kylix 以及 Windows 上的 Delphi CLX 项目中。例如,目前给出了给定演示的结果窗体,如图 6 中所示,该图中显示了 Delphi 7 在设计时显示的结果窗体。该窗体与初始的框架窗体一样,只是添加了 TLabel、TDBImage 和 TDBEdit 控件,事实上,Height 已经被修改,以显示所有控件。

图 6. 设计时生成的新窗体
图 6. 设计时生成的新窗体

正如我上次提到的,所生成的窗体不包含调用 TClientDataSet 的 ApplyUpdates 方法(用以将更新送回数据库)或 UndoLastChange,以及其他 Undo 方法的按钮。这将作为练习留给读者来完成。作为一点小小的一个提示,我建议在 NewForm 中将这些按钮添加至框架窗体,因此,您所做的修改将用于所有新生成的窗体中。那样的话,就可以将 NewForm 视作一个基类,用于所有动态创建的数据库窗体。

结束语
在这篇以及上一篇文章中,都检查了 IBM DB2 Universal Database 元数据,以便动态构建视图和窗体。您已经使用了带有 dbExpress 的 Linux 上的 Borland Kylix 3,以及 Windows 上的 Delphi 7,来分析 DB2 UDB 数据表、字段(名称和类型),允许用户选择特定的表或指定 SQL 语句的 join/where 子句,来回切换应该显示的字段,以及在数据网格(datagrid)和单个数据感知控件中动态查看输出。

最后一步包括动态生成新的 CLX 窗体,以及将该窗体流化为 Delphi .pas 和 .xfm 文件,并准备将它们添加到 Kylix 和 Delphi CLX 项目中(为 DB2 UDB 数据库表产生我们自己定制的数据库窗体向导)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DB2 是一款数据库管理系统,通常用于处理大规模的数据。在 DB2 中,我们可以使用导入命令来将表结构和数据导入到数据库中。具体步骤如下: 1. 创建一个表结构文件 我们可以使用 DB2 提供的 EXPORT 工具将现有的表结构导出到文件中,命令如下: db2 export to 表结构文件.del of del select * from 表名 where 1=2 这个命令将会导出表名为“表名”的表结构到“表结构文件.del”文件中,其中“where 1=2”表示只导出表结构不导出任何数据。 2. 创建一个表数据文件 我们可以使用 DB2 提供的 EXPORT 工具将现有的表数据导出到文件中,命令如下: db2 export to 表数据文件.ixf of ixf select * from 表名 这个命令将会导出表名为“表名”的表数据到“表数据文件.ixf”文件中。 3. 导入表结构和数据 我们可以使用 DB2 提供的 IMPORT 工具将表结构和数据导入到数据库中,命令如下: db2 -tvf 表结构文件.sql db2 "connect to 数据库名 user 用户名 using 密码" db2 load from 表数据文件.ixf of ixf replace into 表名 这个命令将会先执行“表结构文件.sql”中所有的 SQL 语句,以创建表结构。然后使用 IMPORT 工具将“表数据文件.ixf”中的数据导入到数据库中。其中“replace”参数表示如果有重复数据则替换,而不是添加。 以上就是使用 DB2 导入表结构和数据的基本步骤。需要注意的是,导入操作可能会覆盖现有数据,应谨慎操作。建议先备份现有数据再进行导入。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值