DELPHI DATASNAP 2010 入门操作(1)为什么要用datasnap 2010
http://www.cnblogs.com/zhqian/archive/2010/07/06/1771779.html
DELPHI DATASNAP 2010 入门操作(2)不写一行代码,绿色三层我也行
http://www.cnblogs.com/zhqian/archive/2010/07/06/1771798.html
很久前写了点入门的资料,写得很不好,但是发现还是有很多人看了转了, 我写的东西都是很简单的,估计在行业里面混了几年的朋友,肯定会骂,这么没水平的,也好意思放出来?不过对于我来说,这不重要,因为,这是一个学习的过程。
这不,东家又让我们失望了,还记得当初橙子把同时支持linux 及MAC的内部每日更新版本的放出来,给停用了很久的EDN ID,不过还好,听说已经恢复了。 XE出了,因为没有什么功能的更新,群里面的朋友都在戏称delphi XE update 1 为delphi 2010 update 7
在这儿,不去评论XE的好坏,但是传说中的改了4000多个BUG,还是在稳定性方面有所加强的,所以,上一个demo 用的是delphi 2010 做的,今天的DEMO,我改成XE了,向导不太一样了,但是最终生成的代码,没有什么太大的区别,所以,我不再重复向导部份,直接就在上一个DEMO的基础(实际我是全部新做了,上一个DEMO我也不知丢哪儿去了)上,继续我们今天的话题。
说到主从表,在实际工作中还没有用过,不过上次面试时,凭感觉乱拉下控件,居然也做成功能,当然,有很多细节需要处理的,今天,我也关注不了多少细节。
在主从表更新的过程中,有一个最重要的问题就是主建问题,很多人很喜欢用自增ID做主建,因为这个减少我们的代码编写量,但是当在主从表中用了自增ID时,如何在数据提交前就取得对应的ID,可是一个很大的挑战,网上也看到一些解决方案,不过我没有看的太懂,所以,我今天也放弁了自增ID,用GUID 来取而代之,相信用GUID做出来的程序,能够满足你的业务快速发展的需要!
GUID的取值
GUID 按理论上是,其本上不会重复的,我想,不需要我多说吧,不了解的,请谷歌,直接上代码吧
建立GUID的原型代码在 SysUtils 中: 如下:
-
-
- {$IFDEF MSWINDOWS}
- {$EXTERNALSYM CoCreateGuid}
- function CoCreateGuid(out guid: TGUID): HResult; stdcall; external 'ole32.dll' name 'CoCreateGuid';
-
- function CreateGUID(out Guid: TGUID): HResult;
- begin
- Result := CoCreateGuid(Guid);
- end;
- {$ENDIF}
- {$IFDEF POSIX}
- { CreateGUID }
- { libuuid.so implements the tricky code to create GUIDs using the
- MAC address of the network adapter plus other flavor bits.
- libuuid.so is currently distributed with the ext2 file system
- package, but does not depend upon the ext2 file system libraries.
- Ideally, libuuid.so should be distributed separately.
- If you do not have libuuid.so.1 on your Linux distribution, you
- can extract the library from the e2fsprogs RPM.
- Note: Do not use the generic uuid_generate function in libuuid.so.
- In the current implementation (e2fsprogs-1.19), uuid_generate
- gives preference to generating guids entirely from random number
- streams over generating guids based on the NIC MAC address.
- No matter how "random" a random number generator is, it will
- never produce guids that can be guaranteed unique across all
- systems on the planet. MAC-address based guids are guaranteed
- unique because the MAC address of the NIC is guaranteed unique
- by the manufacturer.
- For this reason, we call uuid_generate_time instead of the
- generic uuid_generate. uuid_generate_time constructs the guid
- using the MAC address, and falls back to randomness if no NIC
- can be found. }
- var
- libuuidHandle: NativeUInt;
- uuid_generate_time: procedure (out Guid: TGUID) cdecl;
- function CreateGUID(out Guid: TGUID): HResult;
- const
- E_NOTIMPL = HRESULT($80004001);
- begin
- Result := E_NOTIMPL;
- if libuuidHandle = 0 then
- begin
- // uuid_generate_time lives in libuuid.so.1 on Linux and libc on Mac OSX
- libuuidHandle := dlopen({$IFDEF MACOS}libc{$ENDIF}{$IFDEF LINUX}'libuuid.so.1'{$ENDIF}, RTLD_LAZY);
- if libuuidHandle = 0 then Exit;
- uuid_generate_time := dlsym(libuuidHandle, 'uuid_generate_time');
- if @uuid_generate_time = nil then Exit;
- end;
- if Assigned(uuid_generate_time) then
- begin
- uuid_generate_time(Guid);
- Result := 0;
- end;
- end;
- {$ENDIF POSIX}
- function StringToGUID(const S: string): TGUID;
- procedure InvalidGUID;
- begin
- ConvertErrorFmt(@SInvalidGUID, [s]);
- end;
复制代码
现在,我们就通过调用上面的函数,轻松的实现返回一个GUID,单元如下:
- unit UnitGUID;
-
- interface
- uses sysutils;
-
- function getGUID():string;
- implementation
-
- // 返回一个字符串类型的GUID串
- function getGUID():string;
- var
- Guid:TGUID;
- begin
- CreateGuid(Guid);
- RESULT:=GUIDToString(Guid);
- end;
-
- end.
|
DEMO 数据库的选择
Xe 的DBX 居然连 东家自己的 Blackfish™ SQL 都没有默认驱动了,而DBX又不支持本地库,这不是难为难我麻,要搞个看demo的朋友都了解的库还真不容易,oracle是肯定不会选的,估计没几个人装,而Firebird数据库,估计不是所有人都听过的,MSSQL得装到2008版本,否则还要装个客户端驱动,想了想,算了吧,目前我的需求ADO就满足,为什么就非得用DBx呢,于是,我就选用了ADO+ACCESS来做我的数据库。 数据库结构就不再多说了,这次所做的是一个班级表及一个学生表,关系就是一个学生必须属于一个班级。 |
主从表服务器端设置
TADOQUERY1: name 设置为:ADOQuerymain locktype设置为:ltReadOnly(由于ADO是双向数据集,但是现在我们的数据回写用DSP负责,所以这儿没有必要再设置为可读写,设置为只读可以减少下载数据的总时间) Connection设置为:ADOConnection1
TADOQUERY2: name 设置为:ADOQDetails locktype设置为:ltReadOnly(由于ADO是双向数据集,但是现在我们的数据回写用DSP负责,所以这儿没有必要再设置为可读写,设置为只读可以减少下载数据的总时间) Connection设置为:ADOConnection1 TdataSetProvider1:name设置为:DSPmaindataSET 设置为:ADOQuerymain.options.poAllowCommandText设置为:true TdataSetProvider2:name设置为:DSPDetailsdataSET 设置为:ADOQDetails.options.poAllowCommandText设置为:true options.poAllowCommandText 设置为true 的目的是让客户机能直接向服务器提交代码,这提供了方便,但是也开启了安全隐患之门。 |
设置完成后,再加上一行代码确保连接能连到数据库
我们在ServerMethods1的onCreate事件中写下如下代码:
- procedure TServerMethods1.DSServerModuleCreate(Sender: TObject);
- begin
- ADOConnection1.Connected :=True ;
- end;
复制代码
设计时界面如图:
主从表客户机设置
先设置DBX连接到服务器: 一、运行服务器端 二、DBX连接设置 SQLConnection1 设置 设置Derver为datasnap(由于我们所有设置都是默认,所以代码不需要设置IP、端口等值) LoginPrompt为False 现在我们可以把Connected 设置为TRUE看我们的客户机是否能正常连到服务器
DSProviderConnection1设置: DSProviderConnection设置为: DSProviderConnection1 ServerClassname设置为: TServerMethods1(这个是我们在运行服务器向导时生成的类名)
三、CDS及CDS主从表设置
clientdataset1设置: name设置为 CDSmain RemoteServer设置为:DSProviderConnection1 ProviderName设置为:DSPmain
clientdataset2设置: name设置为 CDSDetails RemoteServer设置为:DSProviderConnection1 ProviderName设置为:DSPDetails MasterSourec设置为:CDSmain MasterFields设置为: 班级编号 IndexFieldNames设置为:班级编号
TDataSource1 设置 name设置为:DataSMain Dataset设置为:CDSmain
TDataSource2 设置 name设置为: DataSDetails Dataset设置为: CDSDetails
DBNavigator1设置 name设置为:DBNavigatorMAIN DataSource设置为:DataSMain
DBNavigator2设置 name设置为:DBNavigatorDetails DataSource设置为:DataSDetails
DBGrid1设置: DataSource设置为:DataSMain 双击控件添加所有班级表字段,其中: 班级编号的readonlay设置为true
DBGrid2设置: DataSource设置为:DataSDetails 双击控件添加所有班级表字段,其中: 学生编号,班级编号,班级名称的readonlay设置为true
放一个工具栏,然后上面放上几个按扭,名称分别设置为:刷新主表,刷新从表,提交主表,提交从表 四、客户端代码编写:
设置完成后,我们开始我们的代码的编写
1、启动时打开数据库:
-
-
- procedure TForm2.FormCreate(Sender: TObject);
- begin
- SQLConnection1.Connected :=True ;
- with CDSmain do
- begin
- Close ;
- CommandText :='select * from 班级 ';
- Open ;
- end;
- with CDSDetails do
- begin
- Close ;
- CommandText :='select * from 学生表 ';
- Open ;
- end;
- end;
复制代码
2、刷新主表代码:
- procedure TForm2.ToolButton1Click(Sender: TObject);
- begin
- CDSmain.Close ;
- CDSmain.Open ;
- end;
复制代码
3、刷新从表代码: procedure TForm2.ToolButton3Click(Sender: TObject); begin CDSDetails.Close ; CDSDetails.Open ; end;
4、在主表添加数据时,自动生成GUID做班级编号
点菜单: file -uses unit... 选择UnitGUID后按ok 在CDSmain的onNewRecord 中添加代码,用于插入时生成GUID:
- procedure TForm2.CDSmainNewRecord(DataSet: TDataSet);
- begin
- with DataSet do
- begin
- FieldByName('班级编号').AsString :=getGUID ;
- end;
- end;
复制代码
5、主表提交时,班级名称不能为空 在CDSmain的onBeforePost 事件中添加代码
- procedure TForm2.CDSmainBeforePost(DataSet: TDataSet);
- begin
- with DataSet do
- begin
- if FieldByName('班级名称').AsString ='' then
- begin
- Application.MessageBox('班级名称不能为空!', '提示信息', MB_OK + MB_ICONINFORMATION +
- MB_TOPMOST);
- Abort ;
- end;
- end;
-
- end;
复制代码
6、在主表中无数据时,不让在从表中插入数据 在CDSDetails的onBeforeInsert 事件中添加代码: procedure TForm2.CDSDetailsBeforeInsert(DataSet: TDataSet); begin if (not CDSmain.Active) or CDSmain.IsEmpty then begin Application.MessageBox('请先录入班级信息再录入学生信息!', '提示信息', MB_OK + MB_ICONINFORMATION + MB_TOPMOST); Abort ; end; end;
7、在从表添加数据时,自动生成GUID做班级编号 在CDSDetails的onNewRecord 中添加代码,用于插入时生成GUID及对主表中的值:
- procedure TForm2.CDSDetailsNewRecord(DataSet: TDataSet);
- begin
- with DataSet do
- begin
- FieldByName('学生编号').AsString :=getGUID ;
- FieldByName('班级编号').AsString :=CDSmain.FieldByName('班级编号').AsString ;
- FieldByName('班级名称').AsString :=CDSmain.FieldByName('班级名称').AsString ;
- end;
- end;
复制代码
8:保存时学生姓名不能为空 在CDSDetails的onBeforePost事件中添加:
- procedure TForm2.CDSDetailsBeforePost(DataSet: TDataSet);
- begin
- with DataSet do
- begin
- if FieldByName('姓名').AsString ='' then
- begin
- Application.MessageBox('学生姓名不能为空!', '提示信息', MB_OK + MB_ICONINFORMATION +
- MB_TOPMOST);
- Abort ;
- end;
- end;
- end;
复制代码
9在提交主表中:
- procedure TForm2.ToolButton5Click(Sender: TObject);
- begin
- try
- CDSmain.ApplyUpdates(0);
- Application.MessageBox('已成功地更新到服务器!', '提示信息', MB_OK + MB_ICONINFORMATION +
- MB_TOPMOST);
- except
- on E:Exception do
- begin
- Application.MessageBox(PChar('更新失败,错误代码为:'+E.Message ), '提示信息', MB_OK + MB_ICONINFORMATION +
- MB_TOPMOST);
- end;
- end;
- end;
复制代码
10 在提交从表中
- procedure TForm2.ToolButton7Click(Sender: TObject);
- begin
- try
- CDSDetails.ApplyUpdates(0);
- Application.MessageBox('已成功地更新到服务器!', '提示信息', MB_OK + MB_ICONINFORMATION +
- MB_TOPMOST);
- except
- on E:Exception do
- begin
- Application.MessageBox(PChar('更新失败,错误代码为:'+E.Message ), '提示信息', MB_OK + MB_ICONINFORMATION +
- MB_TOPMOST);
- end;
- end;
- end;
|
事务处理
如果我们在提交主表失败后再提交从表,哪么从表中的关系就丢失了,就成了垃圾数据了(其实我们的DEMO在删除主表记录时也会产生垃圾数据,这时也要同时删除从表记录,就留给大家来完成了)
如果我们用上事务,就能保证数据正确提交 在服务器的TServerMethods1 类中加上事务处理代码,如下:
-
- unit ServerMethodsUnit1;
- interface
- uses
- SysUtils, Classes, DSServer, DB, ADODB, Provider;
- type
- TServerMethods1 = class(TDSServerModule)
- ADOConnection1: TADOConnection;
- ADOQuerymain: TADOQuery;
- DSPmain: TDataSetProvider;
- ADOQDetails: TADOQuery;
- DSPDetails: TDataSetProvider;
- procedure DSServerModuleCreate(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- function EchoString(Value: string): string;
- function ReverseString(Value: string): string;
- function BeginTrans:Integer ;
- procedure CommitTrans ;
- procedure RollbackTrans ;
- end;
- implementation
- {$R *.dfm}
- uses StrUtils;
- function TServerMethods1.EchoString(Value: string): string;
- begin
- Result := Value;
- end;
- function TServerMethods1.ReverseString(Value: string): string;
- begin
- Result := StrUtils.ReverseString(Value);
- end;
- function TServerMethods1.BeginTrans:Integer;
- begin
- Result :=ADOConnection1.BeginTrans;
- end;
- procedure TServerMethods1.CommitTrans;
- begin
- ADOConnection1.CommitTrans;
- end;
- procedure TServerMethods1.DSServerModuleCreate(Sender: TObject);
- begin
- ADOConnection1.Connected :=True ;
- end;
- procedure TServerMethods1.RollbackTrans ;
- begin
- ADOConnection1.RollbackTrans;
- end;
- end.
复制代码
在客户机的SQLConnection1控件上右击,再选择:Generate Datasnap client classes 会生成我们的相应的调用单元(已存在的会更新)
下载 (30.84 KB)
10 分钟前
定义一个调用类变量
- var
- s:TServerMethods1Client;
复制代码
在我们调用事务前初始化类
- s:= TServerMethods1Client.Create(SQLConnection1.DBXConnection );
复制代码
现在我是在窗口的创建事件中写的代码
- procedure TForm2.FormCreate(Sender: TObject);
- begin
-
- SQLConnection1.Connected :=True ;
- with CDSmain do
- begin
- Close ;
- CommandText :='select * from 班级 ';
- Open ;
- end;
- with CDSDetails do
- begin
- Close ;
- CommandText :='select * from 学生表 ';
- Open ;
- end;
-
- s:= TServerMethods1Client.Create(SQLConnection1.DBXConnection );
- end;
复制代码
在用事务同时更新的单击中添加代码:
- procedure TForm2.ToolButton9Click(Sender: TObject);
- begin
- try
- s.BeginTrans ;//启动事务
- if CDSmain.ChangeCount >0 then
- CDSmain.ApplyUpdates(0);
- if CDSDetails.ChangeCount >0 then
- CDSDetails.ApplyUpdates(0);
- Application.MessageBox('已成功地更新到服务器!', '提示信息', MB_OK + MB_ICONINFORMATION +
- MB_TOPMOST);
- s.CommitTrans ;//提交事务
- except
- on E:Exception do
- begin
- s.RollbackTrans ;//回滚事务
- Application.MessageBox(PChar('更新失败,错误代码为:'+E.Message ), '提示信息', MB_OK + MB_ICONINFORMATION +
- MB_TOPMOST);
- end;
- end;
- end;
复制代码
下载 (66.4 KB)
10 分钟前
|
DAtasnapXEDEMO.rar (175.12 KB)
35 秒前
server.zip (1.47 MB)
2 分钟前
|
附件下载请移步我的论坛:http://datasnap.5d6d.com/thread-117-1-1.html
|
请发表评论