【架構簡介】
*本架構以delphi作為用戶端,Java作為服務端,oracle作為背景資料庫資料。其中delphi用戶端的封裝為了保持與原來的開發方式相容都是基于TclientDataSet實作的;
*本架構選擇基于阻塞模式的Indy通信套件作為通信工具;以tcp/ip作為通信協定 ,直接以流的的形式與Java服務端進行互動;
*本架構的通信過程如下:
請求開始->打包壓縮請求資料->發送請求資料->delphi端阻塞等待->Java端多線程響應請求->解壓解包請求資料->處理請求->打包壓縮處理結果->發送處理結果->
delphi端停止阻塞接收處理結果->解壓解包處理結果->顯示結果
*本架構主要解決以下問題:
1.如何将Java從資料庫中讀出來的資料打包成TClientDataSet可以讀取的資料包;要解決這個問題就需要深入了解TclientDataSet的DataPackage的xml格式。
2.如何将新增-修改-删除後的TClientDataSet資料通過Java儲存到資料庫中;要解決這個問題就需要深入了解TclientDataSet的Delta屬性。
3.如何處理Oracle的大對象字段類型(clob或blob)。
4.delphi端如何調用Java端定義的對應的業務邏輯供。
5.Java端多使用者并發處理效率問題。
6.資料包的格式定義‘打包解包‘壓縮解壓’加密解密問題。
1.【TclientDataSet的DataPackage格式】
TclientDataSet的DataPackage格式一般如下:
-
-
-
-
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 | READONLY
---------------------------------------------------------------------
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”,否則用資料庫相關控件讀出來的隻會是亂碼。
記住位置很重要的。你必須寫成下面的形式:
如果寫成下面的也是不滿足形式良好的規範的:
3.【XML的轉義字元】
在XML語言中,用實體對特殊字元進行轉義.
如果在XML文檔中使用類似"
是以不應該像下面那樣書寫代碼:
if salary < 1000 then
為了避免出現這種情況,必須将字元"
if salary < 1000 then
下面是五個在XML文檔中預定義好的實體:
---------------------
< | < | 小于号
---------------------
> | > | 大于号
---------------------
& | & | 和
---------------------
' | ' | 單引号
---------------------
" | " | 雙引号
---------------------
實體必須以符号"&"開頭,以符号";"結尾。
注意: 隻有"
4.【Oracle的大對象字段類型(clob和blob)】
因為TclientDataSet不支援clob和blob等大對象字段類型,是以我們要使用别的方法來實作相應的讀寫操作;
5.【關于TClientDataSet的Delta屬性】
TClientDataSet.Delta屬性表示目前操作的ClientDataSet中記錄變化的資訊
Delta資訊可以用另外的ClientDataSet來顯示;如myCDS.Data := OrgCDS.Delta
TclientDataSe的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應用伺服器可采用資料庫連接配接池來提高通路效率;