-
可能你不知道的
TClientDatase
t
功能
(
转
)
delphi
学习
2009-12-15
11:34
阅读
69
评论
0
字号:
大
中
小
keyLife
富翁笔记
作者
:
shiningplus
标题
:
delphi
Midas
SQLServer
的自增字段的处理
关键字
:
delphi
Midas
自增字段
分类
:
个人专区
密级
:
公开
(
评分
:
,
回复
:
0,
阅读
:
1830)
??
delphi
Midas
SQLServer
的自增字段的处理
1.
新增时,表中有自增字段,但是不希望用
< br>Refresh
,直接
ApplyUpdates
直接看见自增字段的值
在
< br>pdateRecord
写如下代码
ogateChanges:=True;
procedure
tProvider1AfterUpdateRecord(Sender: TObject;
SourceDS:
TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind:
TUpdateKind);
begin
//DstId
TADODataset
//FId
为自增字段
if
UpdateKind=ukInsert then
begin
dText:='select
@@Identity as FId ';
;
yName('FId').ReadOnly:=False;
yName('FId').Ne
wValue:=yName('FId').AsInteger
;
end;
end;
2.
新增时,从表的关联字段与主表的自增字段同步更新
procedure
tProvider1BeforeUpdateRecord(Sender: TObject;
SourceDS:
TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind:
TUpdateKind; var Applied: Boolean);
begin
//DstProduct
为从表的
Name
//CategoryID
是从表的对于主表的字增自段的关联字段
//
qryIdentity
是
TADOQuery
:='select @@identity'
if (UpdateKind = ukInsert) and
(SourceDS =
DstProduct) and
(yName('CategoryID').Value =
Unassigned)
) then
begin
if
then
begin
;
;
end;
yName('CategoryID').NewValue :=
[0].Value;
end;
end;
Delphi
做为一个快速应用开发工具,
深受程序员的喜爱。
其强大的组件功能,
让程序员能够轻松、
高效地完成常
见的界面开发、数据库应用等
功能。然而,帮
助的相对缺乏,使得许多组件的功能并不为人们正确地使用,究
其原因,仍然是认识上的
问题。对于
MIDAS
开发中的核心部
件,
TClientDataSet
和
TDataSetProvider
,由于
资料的缺乏,人们在网上大多谈论的是李维的书籍内容。我有幸在
BDN
上见到了
Cary
Jensen
的
Professional
< br>Developer
系列文章,详细阐述了
DELPHI
的数据库开发技术。现节选出其中的
ClientDataSe
t
部分,与大家共
同分享。
ClientDataSet
是一个功能强大的类,通过在内存中模拟表
格,实现了其它数据集组件所不具备的强大功能。以
往只在
De
lphi
和
C++ Builder
企
业版中才提供这个组件,如今,
Borland
的全部产品(包
括最新的
Kylix
)都集
成了
TClientDataSet
组件。
p>
TClientDataSet
从类的继承关系上来看,是
TDataSet
这个抽象类的子类,所以我们可以在
< br>TDataSet
这个抽象
层次上对其进行
我们熟悉的操作,比如导航、排序、过滤、编辑。要注意的是,
TClientDataSet
使用了一种全
新的
技术,它将所有的数据均放在内存中,所以
TClientD
ataSet
是个只存在内存中的
“
虚
拟表
”
,因此对数据库
的操作是非常快
的。在
PIII 850
,
512MB
的机器上对十万条记录进行建索引的操作,花费的时间少于半分钟。
与一般的数据集组件不同,
TClientDataSe
t
使用的技术比较特别,本着高速度、低存储需求的原则,
TC
lientDataSet
的内部使
用了两个数据存储源。第一个是其
Data
属性,这是当前内存
数据的视图,反映了所
有的数据改变。如果用户从数据中删除一条记录,则此记录将从<
/p>
Data
中消
失,相应地,加入一条新记录后,此
记录便存在
Data
属性中了。
另一个数据源是
Delta
属性,故名思义,即增量的意思,这个属性反映了对数据的改变。无
论是向
Data
属性新
增还是删除记录
,
都会在
Delta
中
记录下来,
如果是修改了
D
ata
中的记录,
则会在
Delta<
/p>
保存两条相应的记录,
一条是原始记录,另一条仅包含修改的字段
值。正因为
Delta
的存在和
p>
TClientDataSet
在内存中记录数据的特
点,所有的改变都没有立即更新加对应的物理存储中,可以根据这些信息在适当的时候恢复,所以
p>
TClientDataSet
天生具有
缓冲更新功能。
为了使数据更新回数据存储源,我们要调用<
/p>
TClientDataSet
中对应的方法。如果
ClientDataSet
与
< br>DataSetProvider
关联,那么仅需调用
T
ClientDataSet
的
ApplyUpdates
p>
方法即可保存数据的更新,但如果
TCl
ientDataSet
没有对应的
TDataSetProv
ider
存在,而是直接同文件关联,那么,这种方式是非常有趣的,我
们在
BriefCase
模
型中会再次讲解这个问题。此时,如果使用
TClientDataSet
的
SaveT
oFile
和
LoadFromFile
,
都会保留
着
Delta
。调用
MergeChangeLog
和
ClearChanges
后,
Delta
的内容才会被
清空。只是前者是将
< br>Delta
的数据
同
Data
结合起来,将改变存储到物理介质上,而
ClearChanges
则是一股脑
儿全部清空,将数据回
复到原始状态。大部分的应用都是将
TClientData
Set
与
TDataSetProvider
< br>结合使用的。
两者联合使用的行为反映了
Borland
的设计宗旨,就是要提供一个面向分
布式环境的思路。我们下面来慢慢解
释。
当我们将
TClientDataSet
对象的
Active
属性设为
True
< br>或者调用其
Open
方法后,
C
lientDataSet
会向
Da
taSetProvider
发送一个取数据包请求。于是
Da
taSetProvider
便会打开对应的数据集,将记录指针指向第一
条记录,然后从头到
尾依次扫描。对于扫描到的每
一条记录,都会将其编码成一个
variant
数组,我们通常
将它
称之为数据包。完成扫描后,
DataSetProvid
er
会
关闭指向的数据集,并将所有
的这些数据包传递给
ClientDataSet
。在我提供的
演示程序中,你可以清楚地看到这种行为(毕竟眼见为实吗!
)
。程序
主界面右边的
DBGrid<
/p>
连接到一个指向数据库表的数据源,
DataSetProvid
er
即指向此表。当选择了
ClientDataSet |
Load
菜单
项时,你可以看到表格的数据被依次扫描,一旦到
达最后一条记录,表格便会被关闭,右边的
DBGrid
被清空
,
而左边反映
ClientData
Set
数据的
DBGrid
便出显示出
内存中的数据来。
由于这个过程会在
DBGrid
上反映出来,
所以不到
1000
条记录的取出时间
中,大部分都浪费在屏幕的更新显示上
了,你可以选择
ClientDataSet | View
Table
Loading
来禁止显示,而达到加速的目的。
在上面的描述中,我们没有提到一个重要的环节,即数据包是如何还原成表格的。那是因为
DataSetProvider
会
将数据包中
的元数据解码出
来,根据元数据(我们可以理解为数据表的结
构)便可以构造出与物理数据表一模
一样的内存虚拟表。但要注意的是,尽管
DataSetProvider
指向的
数据表可能有多个索引,但这些信息是不会
放在数据包中的,换句话说
,
ClientDataSet
当中的数据默认情况下是无索引
的。但因为
ClientDataSet
具有与
TDataSet
一致的行为,所以我们可以在此基
础上根据需要重建索引。
在
Clie
ntDataSet
中的数据被修改后,
可以提交给物理数据表
持久化这此改变。
这个工作便是由
DataSetProvid
er
完成的。内部
工作原理是:
p>
DataSetProvider
创建一个
TSQLResolver
的实例,这个实例会生成要在底层数据
上执行更改的
SQL
语句。详细地说,就
是对修改日志中的每一条被删除、插入、更改记录生成对应的
SQL
语
句。这个语句的生成也可以由用户控制,
DataSetProvider
的
UpdateMode
属性和
ClientDa
taSet
中的
ProviderFlags
< br>属性都对
SQL
语句的生成有影响。
当然,你也可以换一种方式,即采取同单机或
C/S
p>
结构一样的数据直接操作机制,绕过
SQL
语句和缓冲更新机
制来修改数据库。只需将
< br>ResolveToDataSet
属性设为
True<
/p>
,那么
DataSetProvider
在持久化更新时便不会使
用
TSQLResolve
,而是直接修改
物理数据源。即定位到要删除的
记录,调用删除语句,定位到修改记录,调用
修改语句。我们可以对演示程序稍加修改,
观察此种行为。请将演示程序中的
DataSetProvi
der
的
ResolveToDataSet
< br>属性由
False
改为
True
,运行。在界面中修改数据并且保存,你将会看到右边的导
航按钮
会在瞬间变得可用。
更绝妙的是,
Borland
考虑到了应用的多
样性,为我们提供了
BeforeUpdateRecord
事
件,这样,当
DataSetProvider
对每个修改日志的记录进行操作时,都会触发此事件,我们可以在此事件中加入自己的处理,
如
“
加密操作
”
、
“
商业敏感数据处
理
”
等应用,从而极大地方便了程序员
,让程序员对于数据具有完全的控制能
力。分布式环境的复杂性对数据的存取提出了更高
的要求,所以使用事务来保证数据的
完整性和一致性是非常<
/p>
必要的,
Borland
考虑到了这一点
,当调用
ClientDataSet
的
ApplyUpdates
时,你可以传递一个整数值来指
明
可以容忍的错误数量。如果你的数据非常严格,则可以传递<
/p>
0
值,这样,
DataSetProvi
der
在应用修改时便会打
开一个事务,如果遇到错误,便会<
/p>
回退此事务,修改日志将保持原样,并且将出错的记录标记出来
,最后会触
发
OnReconcileError
事件。
如果传递了一个大于
0
的数,
则当出现的错
误数量小于此
指定值时,
事务会被提交,
发生错误而导致提交失败的记录会保
留在
Delta
中,而提交成功的记录会从修改日志中删除。若
错误数量达到指
定值,
则事务会回退
,结果同整数值为
0
的情况。如果值为负数,则会交所以可提交
的数据都提交,不可提交的
数据仍然保存在修改日志中,并将出错记录标记出来。
虽然,
Borland
是为了满足分布式编程的需要而设计了
TClientDataSet
,但在其它类型的编程环境中使用
ClientDataSet
也具有积极的意义。首先,我们可以看到,由于数据均在内存中进行操作,而且仅在打开数据库 p>
取数据时和将修改持久到回数据库时,才有数据库开销,其它时间数
据库为零,这样就极大地增加了数据库的
负荷,让数据库服务器
能满足更多用户的连接请求。其次,
ClientDataSet
具有其它数据集所不具备的许多高
级
功能,这为程序员进行复杂的编程提供了便利,可以不考虑数据库本身是否支持这此功能,而让
ClientDataSet
去处理这些复杂而繁琐的细节。最
后,
ClientDataSet
< br>在数据存储和应用程序间起到一个抽象层的作用。假如你
的程序使用了
TClientDataSet
,
那么如果你以后
要更改数
据库存储机制。
比如说由<
/p>
BDE
移植到
dbExpress
,
或
者从
ADO
p>
移植到
Interbase Express
,
你的用户界面和数据控制部分几乎就不用改变,
只需要将<
/p>
DataSetProvider
指向新的数据存取组件即可。顺
便说一句,由于缓冲更
新的存在,用户可能非常厌恶调用
p>
ApplyUpdates
操作,
那么你可
以将此调用放入
AfterPost
和
AfterDelte
中,让用户的操作更方
便。
多层
结构中必不可少件
TClientDataSet
的全面剖析<
/p>
2008-12-11 15:01
在三层结构中,
TClientDataSet
的地位是
不可估量的
,她的使用正确与否,是十分关键的,本文从以下几个方面阐述她的使用,希望对你有所帮助
.
1.
动态索引
procedure 1TitleClick(Column: TColumn);
begin
if (not is
Tblobfield)
then//Tblobfield
不能索引,二进制
ieldNames:=ame;
end;
2.
多层结构中主从表的实现
设主表
record
为
-1
,所有记录
设从表
record
为
0
,当前
记录
gates
使用
(
1
)在字段编辑中
add
new field
类型为
aggregates
后设置
expression
(表达试
)
设置
active:=true
即可
使用
dbedit<
/p>
的
field
为前者即可
(
2
)使用
Aggergates
属性
add
设计表达试
调用
showmessage(floattostr());
showmessage([0].Value);
4.
在单层数据库中不要
BDE
p>
使用
ClientDataSet
代替
p>
table,
使用
ClientDataS
et
的
loadfilename
装入
cds
代替
table
的
tablename
的
d
b
或者
dbf
原来的程序改造方法
:
加一个
ClientDataSet
,使用右键
as
sign locate data
后
savetofile
,
再
loadfromfile,
后删
除
table
将原连
table
p>
的
datasource
设为
ClientDataSet
唯一注意的是:要将
拷到
system
或者当前目录
5.
三层结构的公文包的实现方法
同时设定
1:filename(*.cds) server
6.
可以对
data
< br>赋值(从另一个数据集取值
)
:=;
;
或者
ursor(ClientDataSet1,true);
;
7.
附加数据取得
< br>客户程序向应用服务器请求数据。如果
TClientDataSet
的
FetchOnDemand <
/p>
属性设为
True
,
客户程序会根据需要自动检索附加的数据包如
BLOB<
/p>
字段的值或嵌套表的内容。
否则,
客户程序需要显式地调用
GetNextPacket
才能获得这些附加的数据包。
Cli
entDataSet
的
packetrecords
设置一次取得的记录个数
DataSet
p>
与服务器端
query
连接方法
(
1
)
sql
内容为空
;
dText:=edit1.T
ext;//
即
sql
内容
;
对于没有应用服务器设置
filter
如:
country like 'A%'
< br>filtered=true
可实现
sql
功能
(2)
有参数
如服务端
query
的
sql
为
select * from
animals
where name like :dd
则
:
客户端
ClientDataSe
t
var
pm:Tparam;
begin
;
erName:='DataSetProvider1';
pm:=(nil);
:='dd';
pe:=ftString;
;
am(pm);
yName('dd').AsString
:=edit1.T
ext
;
;
end;
9.
数据的更新管理
(1)savepoint
保存目前为止数据状态,可以恢复到这个状态
var
pp:integer;
begin
pp:=int;
;
yName('
姓名
').asstr
ing:='
古话
';
;
h;
end;
恢复点
int:=pp;
(
2
)
cancel,
RevertRecord
取消对当前记录的修改,只适合没有
post
的,如果
post
,调用<
/p>
RevertRecord
(3)cancelupdate
取消对数据库所有的修改
(
4
)
UndoLastChange(bool
ean),changecount
取消上一次的修改,可以实现连续撤消
参数为
true:
光标到恢复处
false:
光标在当前位置不动
<
/p>
changecount
返回修改记录的次数,一个记录修改多次
,返回只一次
但
UndoLastC
hange
只撤消一次
10.
可写的
recno
对于
Ttable
和
Tq
uery
的
recno
是只读的,而<
/p>
TClientDataSet
的
rec
no
可读可写
:=5;
是设第五个记录为当前记录
11.
数据保存
对于
table
使用
post
p>
可更新数据
而
C
lientDataSet1
的
post
只更新内存数据,要更新服务器数据要使用
ApplyUp
dates
(
MaxErrors:
Integer),
他有一个参数,是允许发出错误的
-
-
-
-
-
-
-
-
-
上一篇:InnoDB中文参考手册-性能调整技巧09
下一篇:62-5G系统移动管理基本流程