Delphi中三层主从表设计(摘抄过来的)

摸索了好久,终于找到了3层主从表设置的方法,为了纪念这些天来的努力,记上日记,也为不了解的朋友来学习,如果有什么不对的地方则给我留言。
三层中主从表的操作(删除、新增、修改)一定要在一个事物中完成,那在Delphi中的事物又如何控制呢?我们在开发客户端时,如果为了在一个事物中而用TSqlConnection的事物来控制是徒劳的,没有一点意义,因为真正事物控制是在服务端的TDataSetProvider中产生的,它是自动产生事物的,如果有错误产生,它会回滚事物。

当客户端的ClientdataSet把Delta数据提交给远程的DataSetProvider时,这个DataSetProvider会解析这个语句,并且会自动产生一个事物,所以我们不必要写事物控制,当然用函数时而不用DataSetProvider是另外一个话题。

如果我们直接在远程端设置二个TDataSetProvider,客户端也放两个对就的ClientDataSet,并把客户端的两个数据集设置成主从可以吗?
我的回答是:绝对不可以。为什么?因为你在更新数据时是在两个DataSetProvider中产生二个不同的事物,这样的话就不能保证数据更新的完整性。

Delphi为我们提供的机制是在服务端设置好主从结构,而从表数据集变为主表的一个字段,这样当客户端连接远程的TDataSetProvider时,只把客户端主表数据控件连接到DataSetProvider就可以,从表只要设置一下DataSetField为客户端主表中那个数据集字段就可以了。当保存数据时,只用客户端主表的ApplyUpdata就可以保存主从表数据(可以有多个从表)。更新时就把主从表相关改动的数据传到远程 的DataSetProvider中,些时DataSetProvider可以开启一个事物,这样就能保证数据的完整性。
以下我详细说一下开发主从表的实例:
我用的数据集是:UniDac,它和Ado基本一样用(用mssql2000 northwind 库中的orders和order detail表做实例)
一.开发服务端
1. 放上连接数据库的控件:UniConnection.和MSSQL驱动控件SQLServerUniProvider1(ADO不需要)
2. 放上主表数据集UniQuery1改为名称 是Master,设置它的sql语句为:Select * From Orders
3. 放上主表数据集对应的DataSource,更名为DSMaster,设置这个数据源是为了让从表和它关联
4. 放上从表数据集UniQuery改为名称是Detail,设置它的Sql语句为:Select * from [Order Detail] where orderid=:A(这个形参可以随意设置)
5.设置Detail的MasterSource为DSMaster。再点选Detail属性MasterFields,在弹出的对话框中把主从表的主键及子表的外键关联起来。这样就构成了主从关系结构。
6.在Master的AfterScroll事件中对Detail中的参数A赋值
 procedure
TForm1.MasterAfterScroll(DataSet: TDataSet);
begin
Detail.Close;
Detail.Params[0].Value:=DataSet.FieldByName('orderid').AsInteger;
Detail.Open;
end;
        7.放入名为DSP的DataSetProvider控件,设置其DataSet为Master,并设置ResloveToDataSet为Ture,为Ture表示数据的处理交由和DataSetProvider的数据集来处理,可以处理多表的数据,为False表示产生的数据是由DataSetProvider的内置Sql分析器来处理 ,但不能处理多表数据。且记一定要打开。其它的属性根据需要打开。

下图为从表设置 的MasterFields属性

 





二,客户端设置(为了简便测试,故把服务端和客户端放在同一界面,所以不再设置ClientDataSet的

 

RemoteServer属性)

1.放入名为Main的ClientdataSet控件。


2.设置ClientDataSet的ProviderName为服务端的DSP.


3.在Main上打开字段属性编辑器,并在编辑器中加入服务器端Master语句产生的字段

 

下图为Main上的字段,你会发现有一个Detail的字段,它就是从表数据集,做为一个主表的字段了

 

4.再加入一个名为Child的ClientDataSet控件,它就是在客户端的从表数据集控件。


5. 设置Child的DataSetField为"MainDetail",这是可选的,即它的数据是从Main数据集的Detail字段中得到的,其它的都不用设置。如下图

 

6.分别放两个DataSource和DBgrid,并分别连接到Main和Child数据集和DBGrid中,这样就可以显示数据。

此时,我们就设置好了主从关系,你可以放上一些按钮Open.代码为:Main.Open.你就会发现当客户端主表开启

 

时,从表也跟着开启


如下图


你可以更改、删除、新增了。当然你要控制一下自增主键,这个不再多说。



还有一个发现的情况是子表的 SQL

 

语句中不用参数,如:Select   * from [Order Detail]即可,当然那个主表数据集Master的AfterScroll中的代码就

 

不用要了,其它都不要改变,这样的话也可以和 上面一样的操作。也行!!

 

另就是LookUp字段,如果要让客户端选择的话,则是在客户端的Child中设置它的LookUp字段,而在服务端设置成

 

LookUP字段,则在客户端只显示而不能选择.

自己做的测试DEMO(使用ADO)如下:

object Form1: TForm1
  Left = 56
  Top = 290
  Width = 928
  Height = 480
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnClose = FormClose
  PixelsPerInch = 96
  TextHeight = 13
  object Panel1: TPanel
    Left = 0
    Top = 0
    Width = 920
    Height = 129
    Align = alTop
    TabOrder = 0
    object Button1: TButton
      Left = 264
      Top = 32
      Width = 75
      Height = 25
      Caption = '打开'
      TabOrder = 0
      OnClick = Button1Click
    end
  end
  object DBGridEh1: TDBGridEh
    Left = 0
    Top = 129
    Width = 481
    Height = 317
    Align = alLeft
    DataSource = Ds1
    DynProps = <>
    TabOrder = 1
    object RowDetailData: TRowDetailPanelControlEh
    end
  end
  object DBGridEh2: TDBGridEh
    Left = 481
    Top = 129
    Width = 439
    Height = 317
    Align = alClient
    DataSource = Ds2
    DynProps = <>
    TabOrder = 2
    object RowDetailData: TRowDetailPanelControlEh
    end
  end
  object con1: TADOConnection
    Connected = True
    ConnectionString = 
      'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security In' +
      'fo=False;Initial Catalog=EPOWER;Data Source=.'
    LoginPrompt = False
    Provider = 'SQLOLEDB.1'
    Left = 16
    Top = 8
  end
  object Master: TADODataSet
    Connection = con1
    CursorType = ctStatic
    AfterScroll = MasterAfterScroll
    CommandText = 'select * from EPOWER.dbo.BOMM'
    Parameters = <>
    Left = 96
    Top = 8
  end
  object Detail: TADODataSet
    Connection = con1
    CursorType = ctStatic
    CommandText = 'select * from EPOWER.DBO.BOMD where ParentNo=:ParentNo'
    DataSource = DsMaster
    IndexFieldNames = 'ParentNo'
    MasterFields = 'ParentNo'
    Parameters = <
      item
        Name = 'ParentNo'
        DataType = ftString
        NumericScale = 255
        Precision = 255
        Size = 20
        Value = '4PK-AN-2            '
      end>
    Left = 96
    Top = 72
  end
  object DsMaster: TDataSource
    DataSet = Master
    Left = 168
    Top = 8
  end
  object dsp: TDataSetProvider
    DataSet = Master
    ResolveToDataSet = True
    Left = 168
    Top = 64
  end
  object Main: TClientDataSet
    Aggregates = <>
    Params = <>
    ProviderName = 'dsp'
    Left = 432
    Top = 8
    object MainParentNo: TStringField
      FieldName = 'ParentNo'
      FixedChar = True
    end
    object MainParentName: TWideStringField
      FieldName = 'ParentName'
      Size = 100
    end
    object MainSpecNo: TWideStringField
      FieldName = 'SpecNo'
      Size = 150
    end
    object MainUnit: TWideStringField
      FieldName = 'Unit'
      Size = 10
    end
    object MainTDate: TDateTimeField
      FieldName = 'TDate'
    end
    object MainClassName: TWideStringField
      FieldName = 'ClassName'
      Size = 60
    end
    object MainVersionNo: TStringField
      FieldName = 'VersionNo'
      FixedChar = True
      Size = 2
    end
    object MainLastECN: TStringField
      FieldName = 'LastECN'
      FixedChar = True
      Size = 13
    end
    object MainReMark: TWideStringField
      FieldName = 'ReMark'
      Size = 50
    end
    object MainApprove: TStringField
      FieldName = 'Approve'
      FixedChar = True
      Size = 1
    end
    object MainScrap: TStringField
      FieldName = 'Scrap'
      FixedChar = True
      Size = 1
    end
    object MainModDate: TDateTimeField
      FieldName = 'ModDate'
    end
    object MainModName: TWideStringField
      FieldName = 'ModName'
      Size = 10
    end
    object MainDetail: TDataSetField
      FieldName = 'Detail'
    end
  end
  object Child: TClientDataSet
    Aggregates = <>
    DataSetField = MainDetail
    Params = <>
    Left = 696
    Top = 8
  end
  object Ds1: TDataSource
    DataSet = Main
    Left = 432
    Top = 72
  end
  object Ds2: TDataSource
    DataSet = Child
    Left = 696
    Top = 72
  end
end

  

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, ADODB, ExtCtrls, DBGridEhGrouping, ToolCtrlsEh,
  DBGridEhToolCtrls, DynVarsEh, StdCtrls, EhLibVCL, GridsEh, DBAxisGridsEh,
  DBGridEh, DBClient, Provider;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    con1: TADOConnection;
    Master: TADODataSet;
    Detail: TADODataSet;
    DsMaster: TDataSource;
    dsp: TDataSetProvider;
    Main: TClientDataSet;
    MainParentNo: TStringField;
    MainParentName: TWideStringField;
    MainSpecNo: TWideStringField;
    MainUnit: TWideStringField;
    MainTDate: TDateTimeField;
    MainClassName: TWideStringField;
    MainVersionNo: TStringField;
    MainLastECN: TStringField;
    MainReMark: TWideStringField;
    MainApprove: TStringField;
    MainScrap: TStringField;
    MainModDate: TDateTimeField;
    MainModName: TWideStringField;
    MainDetail: TDataSetField;
    Child: TClientDataSet;
    Ds1: TDataSource;
    Ds2: TDataSource;
    DBGridEh1: TDBGridEh;
    DBGridEh2: TDBGridEh;
    Button1: TButton;
    procedure MasterAfterScroll(DataSet: TDataSet);
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.MasterAfterScroll(DataSet: TDataSet);
begin
   Detail.Close;
   Detail.Parameters[0].Value:=DataSet.FieldByName('ParentNo').AsString;
   Detail.Open;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   Main.Open;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
    i:ShortInt;
begin
   for i:=0 to con1.DataSetCount -1 do
   begin
       con1.DataSets[i].Close;
   end;
      con1.Close;
      Main.Close;
      Child.Close;
end;

end.

 

posted @ 2018-10-23 09:13  MasterQi  阅读(157)  评论(0)    收藏  举报