巧克力燕麦饼干:Delphi三层开发小技巧:TClientDataSet的Delta妙用
来源:百度文库 编辑:偶看新闻 时间:2024/05/09 17:51:41
Delphi三层开发小技巧:TClientDataSet的Delta妙用
Posted on 2011-02-15 00:39 入侵 阅读(187) 评论(0) 编辑 收藏 Delphi做三层开发时,很多人都会在客户端放一个TClientDataSet,中间层远程数据模块就对应放一个TDataSetProvider, 然后再连起来.其实这种方法很烦琐,而且程序痈肿不甘,不好维护.我们都知道TClientDataSet的Delta属性记录了数据的所有修改,应用它我们就可以方便的实现一个单表更新的通用方法.首先,在中间层添加一个方法,就叫ApplyUpdates吧.方法定义如下:
function ApplyUpdates(const UpdateTable:String;Delta:Variant;out err:String):Boolean;
参数UpdateTable是指要更新的表名,Delta是指传过来的TClientDataSet的Delta属性,如果更新错误err返回错误的内容.下面实现这个方法,首先在DataModule上放一个Query,Query连上Connection,然后再放一个 TDataSetProvider连Query.代码如下:
view sourceprint?01
function
TRoDm
.
ApplyUpdates(
const
UpdateTable:
String
;Delta:Variant;out err:
String
):
Boolean
;
02
03
const
sql=
'select * from %s where 1<>1'
;
04
05
var
sqlstr:
string
;
06
07
ErrCount:
Integer
;
08
09
begin
10
11
Result:=
False
;
12
13
sqlstr:=Format(sql,[UpdateTable]);
14
15
try
16
17
Conn
.
BeginTrans;
18
19
Query
.
Close;
20
21
Query
.
sql
.
text:=sqlstr;
22
23
Query
.
open;
24
25
Provider
.
ApplyUpdates(Delta,-
1
,ErrCount);
26
27
Result:=ErrCount=
0
;
28
29
if
Result
then
30
31
Conn
.
CommitTrans
32
33
else
Conn
.
RollbackTrans;
34
35
except
36
37
on
E:Exception
do
38
39
begin
40
41
Conn
.
RollbackTrans;
42
43
err:=E
.
Message;
44
45
end
;
46
47
end
;
48
49
end
;
到此,通用的更新方法已经完成了.不过客户端的ClientDataSet还不能查询显示数据,因此,还要写一个查询方法:
function QuerySQL(const sqlstr:string;out Data:Variant;out err:String):Boolean;
参数sqlstr就是要持行的查询语句,Data返回查询结果,错误时err返回错误消息
QuerySQL实现代码如下:
view sourceprint?01
function
TRoDm
.
QuerySQL(
const
sqlstr:
string
;out Data:Variant;out err:
String
):
Boolean
;
02
03
begin
04
05
Result:=
False
;
06
07
try
08
09
Query
.
close;
10
11
Query
.
sql
.
text:=sqlstr;
12
13
Query
.
sql
.
Open;
14
15
Data:=Provider
.
Data;
16
17
Result:=
True
;
18
19
Except
20
21
on
E:Exception
do
22
23
err:=E
.
Message;
24
25
end
;
26
27
end
;
到这里,中间层的代码已经完了,客户端的调用就简单了.比如客户端有个数据模块DM,上面放一个DcomConnection或者 SocketConnection,名叫Conn.例如,我们现在要做一个商品管理的功能,在窗体上放一个TClientDataSet叫Cds,放 DataSource,DBGrid等,设置好相应的属性.然后在窗体创建(Create事件)时查询回所有数据,代码如下:
view sourceprint?01
const
sql=
'select * from xxxx'
;
02
03
var
Data:Variant;
04
05
err:
String
;
06
07
begin
08
09
if
Dm
.
Conn
.
AppServer
.
QuerySQL(sql,Data,err)
then
10
11
Cds
.
Data:=Data
12
13
else
MessageBox(self
.
handle,
pchar
(
'查询数据出错:'
+err),
'错误'
,MB_OK+MB_ICONERROR);
14
15
end
;
16
17
然后还有"添加","修改","删除"按扭,代码都和我们平时操作一样,比如"添加"按扭的代码:
18
19
cds
.
append;
20
21
cds
.
fieldbyname(
'xxx'
).asinteger:=xxx;
22
23
//....
24
25
cds
.
post;
修改,删除也这样写.不过现在还有个小问题是,这个表的主键的生成问题,这里我们不能用自增主键,要自己自己生成主键,这样你还得在中间层写一个中间层生成主键的方法,在"增加"按扭时生调用生成主键,然后再上面的操作.这里不再多说.
增删改完后,这时的数据还在客户端的内存里,想保存到远程的中间层服务器就要用到我们刚才的方法了,下面就是"保存"按扭下的代码:
view sourceprint?01
var
err:
string
;
02
03
begin
04
05
if
cds
.
ChangeCount=
0
then
exit;
//数据没改变就不用提交了
06
07
if
Dm
.
Conn
.
AppServer
.
ApplyUpdates(
'xxx'
,cds
.
Delta,err)
then
//xxx就是表名了
08
09
begin
10
11
MessageBox(self
.
handle,
'保存成功!'
,
'提示'
,MB_OK+MB_ICONINFORMATION);
12
13
cds
.
MergeChangeLog;
//合并所有改变的数据
14
15
end
else
MessageBox(self
.
handle,
pchar
(
'保存出错:'
+err),
'错误'
,MB_OK+MB_ICONERROR);
16
17
end
;
到此,这篇文章也讲完了.用这个方法,那些单表的基础数据更新还可以写成一个祖先类,只要加一个取得更新表名的虚方法,比如:function TableName:string;virtual;然后其后代只要override这个方法,返回各自的表名,其他的一句代码都不用写.