三层架构delphi+Java+Oracle模式的实现(0)

L

lxchjun

Unregistered / Unconfirmed
GUEST, unregistred user!
【架构简介】交流QQ:110401920注明:三层delphi 架构以delphi作为客户端,Java作为服务端,oracle作为后台数据库数据。其中delphi客户端的封装都是基于TClientDataSet实现的(为了保持与原来的开发方式兼容);中间层不使用MIDAS的远程数据模块,而使用java实现; delphi客户端通过Socket直接以流的的形式与Java服务端进行交互;当数据到了java服务端后,很多优秀的java开源技术就可以根据需要自由使用了;比如多线程,连接池等,甚至可以和jboss,weblogic,websphere等中间件集成来提供服务;架构的通信过程如下: 请求开始->打包压缩请求数据->发送请求数据->delphi端阻塞等待->Java端多线程响应请求->解压解包请求数据->处理请求->打包压缩处理结果->发送处理结果-> delphi端停止阻塞接收处理结果->解压解包处理结果->显示结果 架构主要解决以下问题: 1.如何将Java从数据库中读出来的数据打包成TClientDataSet可以读取识别的数据包;要解决这个问题就需要深入了解TclientDataSet的DataPackage的xml格式。 2.如何将客户端本地新增-修改-删除后的TClientDataSet数据自动生成SQL语句,然后打包发送java端保存到数据库中;要解决这个问题就需要深入了解TclientDataSet的Delta属性。 3.如何处理Oracle的大对象字段类型(clob或blob)。 4.delphi端如何调用Java端定义的对应的业务逻辑供。 5.Java端多用户并发处理效率问题。 6.数据包的格式定义,打包解包,压缩解压,加密解密等问题。 7.其他扩展功能。【架构实现】1.【TclientDataSet的DataPackage格式】TclientDataSet的DataPackage格式一般如下:<?xml version="1.0" standalone="yes" ?> - <DATAPACKET Version="2.0">- <METADATA>- <FIELDS> <FIELD attrname="PORT_ID" fieldtype="fixed" WIDTH="12" /> <FIELD attrname="OP_STATE" fieldtype="fixed" DECIMALS="1" WIDTH="38" /> <FIELD attrname="B_POL" fieldtype="fixed" WIDTH="38" /> <FIELD attrname="PORT_CUSTOMS_CODE" fieldtype="r8" /> <FIELD attrname="B_DEL" fieldtype="fixed" WIDTH="38" /> <FIELD attrname="B_SEA_PORT" fieldtype="r8" /> <FIELD attrname="PORT_AIRCODE" fieldtype="string" SUBTYPE="FixedChar" WIDTH="1" /> <FIELD attrname="PORT_SEACODE" fieldtype="string" SUBTYPE="FixedChar" WIDTH="2" /> <FIELD attrname="CREATE_DATE" fieldtype="dateTime" /> <FIELD attrname="B_BASIC" fieldtype="string" WIDTH="55" /> <FIELD attrname="EDI_CODE3" fieldtype="fixed" WIDTH="38" /> <FIELD attrname="B_LOCAL" fieldtype="fixed" DECIMALS="2" WIDTH="10" /> </FIELDS> <PARAMS /> </METADATA>- <ROWDATA> <ROW PORT_ID="10" OP_STATE="20.0" B_POL="3" /> <ROW PORT_ID="1" OP_STATE="2.0" B_POL="3" /> </ROWDATA> </DATAPACKET> 2.【DataPackage的结构分析】整个XML定义了一个DataPacket;DataPacket包括两个部分:MetaData和RowData。MetaData包括Fields和Params,即包括数据字段定义和数据集参数。RowData为具体记录。 下面详细分析数据字段定义的方法。Field节点的定义主要包括:1>attrname指FieldName 2>fieldtype指字段类型 3>width指需要宽度的字段类型的宽度或数字的有效位数;4>decimals指小数点右边的位数;Oracle主要字段类型具体的对应信息如下表:_____________________________________________________________________OracleType | fieldtype | WIDHT | SUBTYPE | DECIMALS_____________________________________________________________________Char string 1 FixedChar ---------------------------------------------------------------------Char(n) string n FixedChar ---------------------------------------------------------------------Varchar(n) string n ---------------------------------------------------------------------Varchar2(n) string n --------------------------------------------------------------------- data dateTime --------------------------------------------------------------------- Number(s,p) fixed s p ---------------------------------------------------------------------Number(*,p) fixed 38 p ----------------------------------------------------------------------Number(s) fixed s ----------------------------------------------------------------------Number fixed 38 ----------------------------------------------------------------------int fixed 38 ----------------------------------------------------------------------Smallint fixed 38 ----------------------------------------------------------------------Dec(s,p) fixed s p ----------------------------------------------------------------------Float R8 ----------------------------------------------------------------------Real R8 ---------------------------------------------------------------------- 其中用SubType属性来帮助决定类型。表中没有值的单元格,表示该属性没有用到。并且DataPakage中如果想包含中文的话,记得要在第一句的中间加上encoding=”GB2312”,否则用数据库相关控件读出来的只会是乱码。记住位置很重要的。你必须写成下面的形式:<?xml version="1.0" encoding="GB2312" standalone="yes"?>如果写成下面的也是不满足形式良好的规范的:<?xml version="1.0" standalone="yes" encoding="GB2312"?>3.【XML的转义字符】在XML语言中,用实体对特殊字符进行转义.如果在XML文档中使用类似"<" 的字符, 那么解析器将会出现错误,因为解析器会认为这是一个新元素的开始。在XML文档中预定义好了五个实体:小于号(<),大于号(>),和(&),单引号('),双引号(")实体必须以符号"&"开头,以符号";"结尾。注意: 只有"<"字符和"&"字符对于XML来说是严格禁止使用的。剩下的都是合法的,为了减少出错,使用实体是一个好习惯。4.【Oracle的大对象字段类型(clob和blob)】因为TclientDataSet不支持clob和blob等大对象字段类型,所以我们要使用别的方法来实现相应的读写操作;5.【关于TClientDataSet的Delta属性】TClientDataSet.Delta属性表示当前操作的ClientDataSet中记录变化的信息Delta信息可以用另外的ClientDataSet来显示;如myCDS.Data := OrgCDS.DeltaTclientDataSe的UpdateStatus属性反映当前记录的更新状态;
根据更新数据的情况其可能的值分别为: 1.新增一条记录时,在Delta中会有一条记录,标记为usInserted;2.修改记录时,在Delta中对于同一条记录会产生且仅产生两条记录:第一条为原始记录,标记为usUnmodified,第二条只有修改过的字段才有值,标记为usModified 3.刪除记录时,在Delta中会产生一条记录,标记为usDeleted;根据以上Delta属性就可以生成相应的更新SQL语句;6.【数据包的格式定义】6.1.数据包采用变长字符串的方式组织,一般由数据节点和分隔符及结束符组成。具体结构如下图:|------------|-------|------------|-------|------------|-------| ... |-------|| 数据节点 |分隔符 | 数据节点 |分隔符 | 数据节点 |分隔符 | ... |结束符 ||------------|-------|------------|-------|------------|-------| ... |-------| 6.2.数据节点一般由数据长度和数据值组成。6.3.当数据值的长度小于等于999时数据节点的组成如下图:|---------|----------|-------|---------|------------|-------| ... |-------||数据长度 | 数据值 |分隔符 |数据长度 | 数据值 |分隔符 | ... |结束符 ||---------|----------|-------|---------|------------|-------| ... |-------| 6.4.当数据值的长度大于999时数据节点的组成如下图:|-----|---------------|---- |---------|------------|-------| ... |-------||003 |数据长度的长度 | / |数据长度 | 数据值 |分隔符 | ... |结束符 ||-----|---------------|-----|---------|------------|-------| ... |-------| 6.5数据包格式定义说明: 数据长度为数据值长度的长度字符串;数据长度的长度为数据值长度的长度字符串的长度字符串;分隔符一般为冒号;当数据值的长度大于999时分隔符为"/";7.【JAVA应用服务器】应用服务器最重要的就是稳定,支持高效的多用户并发处理;所以应用服务器应该是无状态的;然后是易于部署的;java天生就是用来编写服务器的;成熟的j2ee企业级应用;丰富的开源思想;于是java就成了实现应用服务器的不二选择;JAVA应用服务器具体实现以下功能:1>使用一个ServerSocket监听Delphi客户端发送请求的命令;2>针对每个Delphi客户端发送的请求开启线程解析处理请求;通过JAVA端的多线程来达到高效处理多用户并发的情况。3>通过反射和command设计模式来分派Delphi端请求的相应的业务逻辑对象进行处理;3>通过JDBC与Oracle数据库交互;其实只要数据到了java端,那么只要你愿意你可以选择任意的中间件技术:weblogic,websphere,jboss等等作为应用服务器,而尽想其提供的丰富的管理功能;8.【接口规划】*************************************************************************************************************************函数功能: 发送操作命令和数据到应用服务器函数声明: function StreamCommand(ASendText:WideString;ASendStream:TStream=nil):TStream;stdcall;external Communication;参数说明: ------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- ASendText 要发送的字节数据(一般应包括命令信息) ------------------------------------------------------- ASendStream 要发送的内存流或文件流等数据(默认为nil) -------------------------------------------------------函数说明:正常返回字节流数据(可能返回空串);异常返回nil;此函数一般供DLL中的接口函数调用;
用例:Result:=StreamCommand('013SelectCommand:045SELECT * FROM CRM_CUSTOMER WHERE B_COMPANY=1 :');
这个语句的意思是发送一个查询命令到AppServer,命令的内容为SELECT * FROM CRM_CUSTOMER WHERE B_COMPANY=1;
如果命令能正确执行,那么AppServer会把查询的结果打包发送到客户端,以字节流的形式返回. ************************************************************************************************************************** 函数功能: 查询单个数据集函数声明: function SelectCommand(ACDS: TClientDataSet;const ASelectText: string):Boolean;stdcall;external Communication;
参数说明: ------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- ACDS 存放查询结果集的TClientDataSet ------------------------------------------------------- ASelectText 要发送的单条查询语句 ------------------------------------------------------- 函数说明: 成功返回[true](包括只有数据元的空数据集);失败返回[false] 用例: Result:=SelectCommand(cdsTemp,'SELECT * FROM CRM_CUSTOMER');这个语句的意思是: 发送一个查询语句到AppServer,如果命令能正确执行,那么AppServer会把查询的结果集打包发送到客户端事先创建好的cdsTemp中. *************************************************************************************************************************************函数功能:同时查询多个数据集函数声明:function SelectCommands(ACDS:Array of TClientDataSet;const ASelectText: TStringList):Boolean;stdcall;external Communication;参数说明:------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- ACDS 存放查询结果集的多个TClientDataSet列表 ------------------------------------------------------- ASelectText 要发送的多条查询语句列表 ------------------------------------------------------- 函数说明:成功返回[true](包括只有数据元的空数据集);失败返回[false] 用例:var sSql:string;
sList:TStringList;
cdsTemp1,cdsTemp2,cdsTemp3:TClientDataSet;
begin
try cdsTemp1:=TClientDataSet.Create(nil);
cdsTemp2:=TClientDataSet.Create(nil);
cdsTemp3:=TClientDataSet.Create(nil);
sList:=TStringList.Create;
try sSql:='SELECT * FROM Table1';
sList.Add(sSql);
sSql:='SELECT * FROM Table2';
sList.Add(sSql);
sSql:='SELECT * FROM Table3';
sList.Add(sSql);
//把第一条查询语句的结果集存放到cdsTemp1,把第二条查询语句的结果集存放到cdsTemp2,依次类推存放顺序 SelectCommands([cdsTemp1,cdsTemp2,cdsTemp3],sList);
finally if Assigned(cdsTemp1) then
FreeAndNil(cdsTemp1);
if Assigned(cdsTemp2) then
FreeAndNil(cdsTemp2);
if Assigned(cdsTemp3) then
FreeAndNil(cdsTemp3);
if Assigned(sList) then
FreeAndNil(sList);
end;
except end;
end;
**************************************************************************************************************************函数功能: 发送一条或多条update or insert or delete类型的SQL语句到应用服务器执行函数声明: function ExecuteCommands(const AExecuteText: TStringList): integer;stdcall;external Communication;参数说明: ------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- AExecuteText AppServer能解析的SQL语句 ------------------------------------------------------- 函数说明: 正常返回0;异常返回非0;本函数主要供ApplyUpdates函数调用;
用例: **************************************************************************************************************************函数功能: 发送一条update or insert or delete类型的SQL语句到应用服务器执行函数声明: function ExecuteCommand(const ACommandText:WideString):integer;
stdcall;external Communication;参数说明: ------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- ACommandText 要执行的SQL语句 ------------------------------------------------------- 函数说明: 正常返回0;异常返回非0;
用例: **************************************************************************************************************************函数功能: 根据数据集列表的修改信息自动生成相应的SQL语句函数声明: function CreateUpdates(const ATableNames: array of string ;ACDS:array of TClientDataSet;var sSqlList:string): Boolean;stdcall;
external Communication;参数说明: ------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- ATableNames 数据集列表对应的表名列表 ------------------------------------------------------- ACDS 修改过的数据集列表 ------------------------------------------------------- sSqlList 存放对应生成的SQL语句 ------------------------------------------------------- 函数说明: 成功返回[true];失败返回[false] 本函数根据数据集中的修改信息自动生成Insert,Update,Delete类型的多条SQL语句. 用例: 1>Result:=CreateStatement(['TableName1','TableName2','TableName3','TableName4'],[cds1,cds2,cds3,cds4]);
或者 2>Result:=CreateStatement(['TableName1'],[cds1]);
本用例会根据数据集列表中每个数据集的修改信息自动生成相应的SQL语句,这样就完成了操作界面到SQL语句之间的直接映射. **************************************************************************************************************************函数功能: 更新多个数据集函数声明: function ApplyUpdates(const ATableNames: array of string ;
ACDS:array of TClientDataSet): Boolean;stdcall;
external Communication;参数说明: ------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- ACDS 要更新的数据集列表 ------------------------------------------------------- ATableNames 要更新的表名列表 ------------------------------------------------------- 函数说明: 成功返回[true];失败返回[false] 表名列表与数据集列表应该一一对应 用例: Result:=ApplyUpdates(['TableName1','TableName2','TableName3','TableName4'], [cds1,cds2,cds3,cds4]);
本用例会根据数据集列表中每个数据集的修改信息自动生成相应SQL语句,然后把所有的SQL语句一起发送到AppServer进行事务处理.*****************************************************************************************************************函数功能: 保存Oracle大对象字段类型(clob或blob)函数声明: function SaveBlob(SqlText:WideString;LobID:WideString;LobContent:TStream):integer;stdcall;
external Communication;参数说明: ------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- SqlText 保存前要执行的SQL语句 ------------------------------------------------------- LobID 包含Oracle大对象字段类型的记录的ID ------------------------------------------------------- LobContent 要保存的Oracle大对象字段类型的内容 ------------------------------------------------------- 函数说明: 成功返回[0];失败返回[非0] 用例:*****************************************************************************************************************函数功能: 显示Oracle大对象字段类型(clob或blob)函数声明: function SelectBlob(LobID:WideString):TStream;stdcall;
external Communication;参数说明: ------------------------------------------------------- 参数名称 描述 ------------------------------------------------------- LobID 包含Oracle大对象字段类型的记录的ID -------------------------------------------------------函数说明: 成功返回Oracle大对象字段类型对应的流;失败返回[nil];
用例:9.【扩展功能】1>客户端数据缓存机制保证运行的高效性:客户端可以缓存大量的客户端数据,并提供了一定程度的离线操作功能;
这样在提高户交互效率的同时,减少网络数据通讯量;还能降低服务器的负载。 2>客户端自动更新机制:便于客户端的部署和版本更新3>权限管理:包括功能权限和数据权限;4>客户端采用模块化(DLL)设计保证系统的可扩展性;5>运行时自定义报表;6>JAVA应用服务器可采用数据库连接池来提高访问效率;
 
强,想和你学学。
 
你發這么長出來的意思是什么呢、???中間 用 Socket 鏈接不好 使用 Soap 鏈接好些。利用WSDL文件 ,中間用webservice , java 些方法。。數據傳輸用 XML 數據形式, 壓縮 —— 發送 —— 解壓 —— 解析XML 。。。。
 
3.如何处理Oracle的大对象字段类型(clob或blob)。这个问题应该使用 base64编码就可以了小于号(<),大于号(>),和(&),单引号('),双引号(")这五个实体怎么使用。 请楼主做个例子!
 
放狗找到的,原来是这样:XML常用转义字符:XML常用转义字符:( & &amp;
< < > > " &quot;
' &apos;
)居然发不上来! 发上来就被打回原型了!http://hi.baidu.com/dongkundjy/blog/item/523fd836ae48e9d4a2cc2b43.html打开这个连接就可以看到了!
 
很想了解,加了QQ。
 
不错,顶
 
想法不错,类似想法也做过一些案例,想探讨一个小问题,就是delta分析中,如果一个字段把旧值改为新值 空(NULL 或者 '')时,不知道怎么判断此字段是变为空还没变,实际经验中这点不知道你如何处理? 可以验证下:把某字端变为空,和没变的delta结果几乎一样,不知道是我没发现区别还是就根本无法区分?一直疑惑。。。。
 
to: xj_lq 这个问题我遇到了,我的处理办法是直接分析TClientDataSet的Data属性,这样效率有一点点降低,但一般都是没问题的;因为仅仅分析delta是判断不出来的,除非修改midas.dll的源码在delta里标记是那种情况(我正在分析midas.dll的源码,下一个版本准备这样做),
 
我自己开发了一个中间层服务器,已完全替换了MIDAS的SocketServer,客户端也是用TClientDataSet服务端可根据Delta自动产生更新用语句,同时还具备任务计划、文件传输,消息通讯、短信收发的功能,作为一个一体化的集成服务器,不可不是用JAVA写的。
 

Similar threads

S
回复
0
查看
444
swish
S
回复
0
查看
810
不得闲
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
顶部