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.

浙公网安备 33010602011771号