採用服務器返回數據,一種是返回字符串數據例如JSON,跨平臺跨語言,任何語言調用都支持兼容,相似WEBService。javascript
第二種是緊密結合c++builder語言,傳輸DataSet,能夠是ClientDataSet,也能夠是FDMemTable,或TDataSet,這類好處是DataSet自己包含了不少屬性,記錄集的狀態Insert/Modify/Delete,在服務端能夠調用php
不一樣的方法進行數據處理,客戶端也只須要把dataset對象賦值就展現到dbgrid裏了。html
序列化。java
FDMemTable1有saveToFile(sfJson),雖然是json格式,但不能跨語言交流,只能fdMem.LoadFromFile纔可解析。因此用這個json字符串是不通用的,只能RAD delphi/c++使用。c++
http://localhost:8083/datasnap/rest/TServerMethods1/GetDepartmentNamesJSONgit
Tokyo 10.2.2有了TFDBatchMoveJSONWritergithub
https://community.embarcadero.com/blogs/entry/dataset-mapping-to-json-for-javascript-client-support-in-rad-studio-10-2-2web
對返回的數據增刪改查。對應的方法就是Add/Delete/Update/Query,客戶端調用此方法就OK了。sql
數據集轉換依賴於TDBXReader、TDBXCommand、SQLConnection1。不能隨意轉換dataset爲json。有個第三方的庫能夠轉。數據庫
Serever
String GetPersonAll()
{
return "";字符串形式的JSON或XML格式
aReader:=aCommand
.
ExecuteQuery;
Result:=TDBXJSONTools
.
TableToJSON(aReader,
10
,
True
).ToString;
}
Client
String DataSTR=srv->GetPersonAll();
對字符串解析JSON或XML,以DataSet展現就能夠。
function TServerMethods1.GetData(SQL: String): String; var aCommand: TDBXCommand; aReader: TDBXReader; begin Result := ''; aCommand := SQLConnection1.DBXConnection.CreateCommand; // 指定 SQLConnection1 鏈接數據庫 try aCommand.Text := SQL; // 指定SQL語句 aReader := aCommand.ExecuteQuery; Result := TDBXJSONTools.TableToJSON(aReader, 10, True).ToString; finally aCommand.Free; end; end;
用第三方庫SuperJson也能夠。
http://blog.csdn.net/ddqqyy/article/details/6982164 利用TDBXDataSetReader實例化,傳輸的是TDBXReader
http://blog.csdn.net/ddqqyy/article/details/6174525 講的是返回dataset ClientDataSet1.Delta,TDataSetProvider,TSqlServerMethod,還用到了OleVariant
#include <Data.DBXCDSReaders.hpp>
static void __fastcall CopyReaderToClientDataSet(Data::Dbxcommon::TDBXReader* Reader, Datasnap::Dbclient::TClientDataSet* Dataset);
static Datasnap::Dbclient::TClientDataSet* __fastcall ToClientDataSet(TComponent* AOwner, Data::Dbxcommon::TDBXReader* Reader, bool AOwnsInstance);
C++中這些返回指針,怎麼釋放一直沒想清楚。
int TServerMethods1::GetTableDataSet(String atableName, OleVariant adata)
{
ClientDataSet1->Open();
adata = this->ClientDataSet1->Data;
return this->ClientDataSet1->RecordCount;
}
DataSetProvider1也有Data屬性。
OleVariant Data
DataSetProvider1->Data;
只能用ClientDataSet1,其餘的數據集都沒有data屬性,有data屬性的FDQuery類型也不是OleVariant 。
ClientDataSet:OleVariant Data
FDQuery:_di_IFDDataSetReference Data
TDBXReader 能夠本身釋放內存,因此沒必要擔憂心裏釋放泄露的問題了。
#include "Data.DBXCDSReaders.hpp"
TDBXReader* TServerMethods1::GetDSReader(String asql) { TDBXCommand *cmd; TDBXReader *reader; TClientDataSet *cds; cmd = SQLConnection1.DBXConnection.CreateCommand; cmd->Text = asql; reader = cmd->ExecuteQuery();
return reader;
//or cds = TDBXClientDataSetReader::ToClientDataSet(NULL, reader, true); return new TDBXClientDataSetReader(cds, true); }
Client
reader= CallProxy.GetDSReader();
TDBXClientDataSetReader::CopyReaderToClientDataSet(reader,cds);
cds->Open();
服務端:
fdquery
DataSetProvider1.DataSet=fdquery;
DataSetProvider1.Option.poAllowCommandText=true;
客戶端
SQLConnection1
TDSProviderConnection.sqlconnection=SQLConnection1
TDSProviderConnection.serverclassname =tServerMethods1
ClientDataSet.RemoteServer=DSProviderConnection1
ClientDataSet.providername =DataSetProvider1
ClientDataSet.open();
ok!
TFDJSONDataSets能夠返回多個數據集。這個是之後的趨勢!REST Client,可是這個不能跨語言,客戶端只能用RAD的TFDJSONDataSetsReader解析。但依然不能跨語言,C#,java沒法解析。
服務的沒有釋放TFDJSONDataSets總感受有內存泄漏,調用1000次測試一下。
返回字符串沒有內存泄漏。
server
function TServerMethods1.GetJSONData: TFDJSONDataSets; begin Result := TFDJSONDataSets.Create; if not FDMemTable1.Active then FDMemTable1.LoadFromFile('../../customer.fds') else FDMemTable1.Active := False; TFDJSONDataSetsWriter.ListAdd(Result, FDMemTable1); end;
Client
var DSList: TFDJSONDataSets; begin FDMemTable1.Close; DSList := ClientModule1.ServerMethods1Client.GetJSONData; FDMemTable1.AppendData( TFDJSONDataSetsReader.GetListValue(DSList, 0)); FDMemTable1.Open; end;
or serverProxy eg
FUnMarshal := TDSRestCommand(FGetDepartmentNamesCommand.Parameters[0].ConnectionHandler).GetJSONUnMarshaler;
Result := TFDJSONDataSets(FUnMarshal.UnMarshal(FGetDepartmentNamesCommand.Parameters[0].Value.GetJSONValue(True)));
客戶端 也能夠 FDMemTableDepartments.Data := TFDJSONDataSetsReader.GetListValue(LDataSetList, 0);
未實行
此方法雖然返回了TJSONObject,但依然不能跨語言,C#,java沒法解析。
DataSet2JsonObject
從DataSet到TJsonObject
http://community.embarcadero.com/blogs/entry/cbuilder-xe6-multitier-database-app-with-firedac-json-reflection-40330
DataSetsToJSONObject
TJSONObject * TServerMethods1::GetDepartmentEmployeesJSON ( System::UnicodeString AID ) { FDQueryDepartmentEmployees->Active = false; FDQueryDepartment->Active = false; FDQueryDepartment->Params->operator[ ]( 0 )->Value = AID; FDQueryDepartmentEmployees->Params->operator[ ]( 0 )->Value = AID; // Create dataset list TFDJSONDataSets * ds = new TFDJSONDataSets( ); // Add departments dataset TFDJSONDataSetsWriter::ListAdd( ds, sDepartment, FDQueryDepartment ); // Add employees dataset TFDJSONDataSetsWriter::ListAdd( ds, sEmployees, FDQueryDepartmentEmployees ); TJSONObject * obj = new TJSONObject( ); TFDJSONInterceptor::DataSetsToJSONObject( ds, obj ); return obj; }
服務的沒有釋放TFDJSONDataSets總感受有內存泄漏,調用1000次測試一下。
返回字符串沒有內存泄漏。
TFDJSONDataSets
C++Builder
http://blogs.embarcadero.com/pawelglowacki/2014/06/04/40330/
Delphi
http://blogs.embarcadero.com/fernandorizzato/index.php/2014/07/21/multi-tier-com-delphi-xe6-e-firedac-json-reflection/
http://www.cnblogs.com/hnxxcxg/p/4007876.html
http://www.cnblogs.com/hnxxcxg/p/4008789.html
http://www.kzx123.com/?p=105
http://blog.marcocantu.com/blog/delphi_xe5_update2_datasnap_firedac.html
D:\Users\Public\Documents\Embarcadero\Studio\15.0\Samples\Object Pascal\DataSnap\FireDACJSONReflect
TFDJSONDataSets *dset;
TFDJSONDataSetsReader * dsread;
TFDJSONDataSetsWriter::ListAdd(dset, FDMemTable1);//FDQuery1
TFDJSONDataSets 是DataSet的集合,數據集的集合,能夠持有多個DataSet。
FDQuery1,FDMemTable1其實也能夠有多個數據集,也算是數據集的集合,取下一個數據集FDQuery1.NextRecordSet
FireDAC默認是不支持多結果集返回的, 須要手動設置 FetchOptions.AutoClose := False;
下面sql返回2個數據集。
FDQuery1.SQL.Text := 'select * from orders; select * from customers';
Data.FireDACJSONReflect.hpp
返回值能夠是 TFDJSONDataSets,TJSONObject*,TFDJSONDeltas
經過 TFDJSONInterceptor::DataSetsToJSONObject()把TFDJSONDataSets轉爲TFDJSONDataSets。
static bool __fastcall DataSetsToJSONObject(TFDJSONDataSetsBase* const ADataSets, System::Json::TJSONObject* const AJSONObject); static bool __fastcall JSONObjectToDataSets(System::Json::TJSONObject* const AJSONObject, TFDJSONDataSetsBase* const ADataSets); static int __fastcall ListApplyUpdates(TFDJSONDeltas* const ADeltaList, const System::UnicodeString AKey, Firedac::Comp::Client::TFDCustomCommand* const ASelectCommand, TFDJSONErrors* const AErrors = (TFDJSONErrors*)(0x0))/* overload */; static Firedac::Comp::Client::TFDAdaptedDataSet* __fastcall GetListValueByName(TFDJSONDataSets* const ADataList, const System::UnicodeString AName);
void __fastcall TForm2::FormCreate(TObject *Sender) { //Server dataSet > JSONObj TFDJSONDataSets *dss; TFDJSONDataSetsWriter::ListAdd(dss, FDMemTable1); TFDJSONDataSetsWriter::ListAdd(dss, "employ", FDMemTable1); TJSONObject *jsobj = new TJSONObject(); TFDJSONInterceptor::DataSetsToJSONObject(dss, jsobj); //Client JSONObject > dataSet std::auto_ptr<TFDJSONDataSets>LDataSets(new TFDJSONDataSets()); TFDJSONInterceptor::JSONObjectToDataSets(jsobj, LDataSets.get()); FDMemTable1->Active = false; TFDAdaptedDataSet * LDataSet = TFDJSONDataSetsReader::GetListValue(LDataSets.get(), 0); FDMemTable1->AppendData(*LDataSet); // client updateData TFDJSONDeltas *LDeltas; TFDJSONDeltasWriter::ListAdd(LDeltas, "a", FDMemTable1); TFDJSONInterceptor::DataSetsToJSONObject(LDeltas, jsobj); // dm->ServerMethods1Client->ApplyChangesDepartmentEmployeesJSON(LJSONObject); //Server updata DataBase Server TFDJSONInterceptor::JSONObjectToDataSets(jsobj, LDeltas); TFDJSONErrors *errs; TFDJSONDeltasApplyUpdates::ListApplyUpdates(LDeltas, "a", FDQuery1->Command, errs) }
更新還沒實驗好
TFDAdaptedDataSet * LDataSet = TFDJSONDataSetsReader::GetListValueByName(LDataSets.get(), sDepartment);
// Update UI
FDMemTableDepartment->Active = False;
FDMemTableDepartment->AppendData(*LDataSet);
fdstoreproc 執行返回數據
FDStoredProc1.Close;
FDStoredProc1.Unprepare;
FDStoredProc1.StoredProcName := 'TServerMethods1.QueryData';
FDStoredProc1.Prepare;
FDStoredProc1.ParamByName('sql').Value := 'select * from MyTable';
FDStoredProc1.open;
FDMemTable1.Close;
FDMemTable1.Data := FDStoredProc1.Data
#include "Data.DBXCommon.hpp"
TDBXCommand *FFindDataSetCommand;
內存釋放FreeOnExecute有此函數貌似安全了。
FreeOnExecute registers the object indicated by Value
, and frees it the next time the command is executed, closed, or freed.
返回TDataSet*
TDataSet* __fastcall TServerMethods1Client::FindDataSet(System::UnicodeString sql) { if (FFindDataSetCommand == NULL) { FFindDataSetCommand = FDBXConnection->CreateCommand(); FFindDataSetCommand->CommandType = TDBXCommandTypes_DSServerMethod; FFindDataSetCommand->Text = "TServerMethods1.FindDataSet"; FFindDataSetCommand->Prepare(); } FFindDataSetCommand->Parameters->Parameter[0]->Value->SetWideString(sql); FFindDataSetCommand->ExecuteUpdate(); TDataSet* result = new TCustomSQLDataSet(NULL, FFindDataSetCommand->Parameters->Parameter[1]->Value->GetDBXReader(False), True); result->Open(); if (FInstanceOwner) FFindDataSetCommand->FreeOnExecute(result); return result; }
server code
// Get a Department and all Employees in the department. Return TJSONObject.
function TServerMethods1.GetDepartmentEmployeesJSON(const AID: string)
: TJSONObject;
var
LDataSets: TFDJSONDataSets;
begin
LDataSets := GetDepartmentEmployees(AID);
try
Result := TJSONObject.Create;
TFDJSONInterceptor.DataSetsToJSONObject(LDataSets, Result)
finally
LDataSets.Free;
end;
end;
client code
TJSONObject* __fastcall TServerMethods1Client::FindDataSetJSObj(System::UnicodeString sql) { if (FFindDataSetJSObjCommand == NULL) { FFindDataSetJSObjCommand = FDBXConnection->CreateCommand(); FFindDataSetJSObjCommand->CommandType = TDBXCommandTypes_DSServerMethod; FFindDataSetJSObjCommand->Text = "TServerMethods1.FindDataSetJSObj"; FFindDataSetJSObjCommand->Prepare(); } FFindDataSetJSObjCommand->Parameters->Parameter[0]->Value->SetWideString(sql); FFindDataSetJSObjCommand->ExecuteUpdate(); TJSONObject* result = (TJSONObject*)FFindDataSetJSObjCommand->Parameters->Parameter[1]->Value->GetJSONValue(FInstanceOwner); return result; }
返回Stream
Result := FFileDownloadCommand.Parameters[1].Value.GetStream(FInstanceOwner);
update json
// Update department and employees using deltas. TJSONObject parameter.
procedure TServerMethods1.ApplyChangesDepartmentEmployeesJSON(const AJSONObject
: TJSONObject);
var
LDeltas: TFDJSONDeltas;
begin
LDeltas := TFDJSONDeltas.Create;
TFDJSONInterceptor.JSONObjectToDataSets(AJSONObject, LDeltas);
ApplyChangesDepartmentEmployees(LDeltas);
end;
Skip to content This repository Explore Features Enterprise Pricing 1 0 0 flrizzato/FireDACJSONReflectionXE7 FireDACJSONReflectionXE7/ServerMethodsUnit1.pas Git User on 15 Oct 2014 first version 0 contributors 219 lines (188 sloc) 6.751 kB // // FireDACJSONReflect demo // Copyright (c) 1995-2013 Embarcadero Technologies, Inc. // You may only use this software if you are an authorized licensee // of Delphi, C++Builder or RAD Studio (Embarcadero Products). // This software is considered a Redistributable as defined under // the software license agreement that comes with the Embarcadero Products // and is subject to that software license agreement. // unit ServerMethodsUnit1; interface uses System.SysUtils, System.Classes, Datasnap.DSServer, Datasnap.DSAuth, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt, FireDAC.UI.Intf, FireDAC.VCLUI.Wait, FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Phys, Data.DB, FireDAC.Comp.Client, FireDAC.Phys.IBBase, FireDAC.Phys.IB, FireDAC.Comp.UI, FireDAC.Comp.DataSet, Data.FireDACJSONReflect, System.JSON, FireDAC.Stan.StorageBin, FireDAC.Stan.StorageJSON, FireDAC.Phys.IBDef; type {$METHODINFO ON} TServerMethods1 = class(TDataModule) FDQueryDepartmentEmployees: TFDQuery; FDQueryDepartment: TFDQuery; FDQueryDepartmentNames: TFDQuery; FDGUIxWaitCursor1: TFDGUIxWaitCursor; FDPhysIBDriverLink1: TFDPhysIBDriverLink; FDConnectionEMPLOYEE: TFDConnection; FDStanStorageJSONLink1: TFDStanStorageJSONLink; FDQueryDepartmentDEPT_NO: TStringField; FDQueryDepartmentDEPARTMENT: TStringField; FDQueryDepartmentHEAD_DEPT: TStringField; FDQueryDepartmentMNGR_NO: TSmallintField; FDQueryDepartmentBUDGET: TBCDField; FDQueryDepartmentLOCATION: TStringField; FDQueryDepartmentPHONE_NO: TStringField; procedure DataModuleCreate(Sender: TObject); public { Public declarations } function EchoString(Value: string): string; function ReverseString(Value: string): string; // Strongly typed methods function GetDepartmentNames: TFDJSONDataSets; function GetDepartmentEmployees(const AID: string): TFDJSONDataSets; procedure ApplyChangesDepartmentEmployees(const ADeltaList: TFDJSONDeltas); // Equivalent TJSONObject methods (C++ compatible) function GetDepartmentNamesJSON: TJSONObject; function GetDepartmentEmployeesJSON(const AID: string): TJSONObject; procedure ApplyChangesDepartmentEmployeesJSON(const AJSONObject : TJSONObject); function FileDownload(sFileName: string): TStream; procedure FileUpload(fStream: TStream); end; {$METHODINFO OFF} implementation {$R *.dfm} uses System.StrUtils, System.Generics.Collections; const sDepartment = 'Department'; sEmployees = 'Employees'; sDataDir = 'c:\temp\'; // Get a Department and all Employees in the department. Result TFDJSONDataSets. function TServerMethods1.GetDepartmentEmployees(const AID: string) : TFDJSONDataSets; begin // Clear active so that query will reexecute FDQueryDepartmentEmployees.Active := False; FDQueryDepartment.Active := False; FDQueryDepartment.Params[0].Value := AID; FDQueryDepartmentEmployees.Params[0].Value := AID; // Create dataset list Result := TFDJSONDataSets.Create; // Add departments dataset TFDJSONDataSetsWriter.ListAdd(Result, sDepartment, FDQueryDepartment); // Add employees dataset TFDJSONDataSetsWriter.ListAdd(Result, sEmployees, FDQueryDepartmentEmployees); end; // Get a Department and all Employees in the department. Return TJSONObject. function TServerMethods1.GetDepartmentEmployeesJSON(const AID: string) : TJSONObject; var LDataSets: TFDJSONDataSets; begin LDataSets := GetDepartmentEmployees(AID); try Result := TJSONObject.Create; TFDJSONInterceptor.DataSetsToJSONObject(LDataSets, Result) finally LDataSets.Free; end; end; // Update department and employees using deltas. TFDJSONDeltas parameter. procedure TServerMethods1.ApplyChangesDepartmentEmployees(const ADeltaList : TFDJSONDeltas); var LApply: IFDJSONDeltasApplyUpdates; begin // Create the apply object LApply := TFDJSONDeltasApplyUpdates.Create(ADeltaList); // Apply the department delta LApply.ApplyUpdates(sDepartment, FDQueryDepartment.Command); if LApply.Errors.Count = 0 then // If no errors, apply the employee delta LApply.ApplyUpdates(sEmployees, FDQueryDepartmentEmployees.Command); if LApply.Errors.Count > 0 then // Raise an exception if any errors. raise Exception.Create(LApply.Errors.Strings.Text); end; // Update department and employees using deltas. TJSONObject parameter. procedure TServerMethods1.ApplyChangesDepartmentEmployeesJSON(const AJSONObject : TJSONObject); var LDeltas: TFDJSONDeltas; begin LDeltas := TFDJSONDeltas.Create; TFDJSONInterceptor.JSONObjectToDataSets(AJSONObject, LDeltas); ApplyChangesDepartmentEmployees(LDeltas); end; procedure TServerMethods1.DataModuleCreate(Sender: TObject); begin end; // Get all Departments. Result TFDJSONDataSets. function TServerMethods1.GetDepartmentNames: TFDJSONDataSets; begin // Clear active so that query will reexecute FDQueryDepartmentNames.Active := False; Result := TFDJSONDataSets.Create; TFDJSONDataSetsWriter.ListAdd(Result, FDQueryDepartmentNames); end; // Get all Departments. Result TFDJSONDataSets; function TServerMethods1.GetDepartmentNamesJSON: TJSONObject; var LDataSets: TFDJSONDataSets; begin LDataSets := GetDepartmentNames; try Result := TJSONObject.Create; TFDJSONInterceptor.DataSetsToJSONObject(LDataSets, Result); finally LDataSets.Free; end; end; function TServerMethods1.EchoString(Value: string): string; begin Result := Value; end; function TServerMethods1.FileDownload(sFileName: string): TStream; var fFileStream: TFileStream; begin fFileStream := TFileStream.Create(sDataDir + sFileName, fmOpenRead); Result := fFileStream; end; procedure TServerMethods1.FileUpload(fStream: TStream); var fMemStream: TMemoryStream; BytesRead: integer; sFileName: string; Buffer: PByte; const MaxBufSize = 1024 * 1024; begin sFileName := sDatadir + 'file-to-upload-copy.jpg'; DeleteFile(sFileName); if not FileExists(sFileName) then begin fMemStream := TMemoryStream.Create; try fStream.Seek(0, TSeekOrigin.soBeginning); fStream.Position := 0; GetMem(Buffer, MaxBufSize); repeat BytesRead := fStream.Read(Buffer^, MaxBufSize); if BytesRead > 0 then fMemStream.WriteBuffer(Buffer^, BytesRead); until BytesRead < MaxBufSize; fMemStream.Seek(0, TSeekOrigin.soBeginning); fMemStream.SaveToFile(sFileName); finally fMemStream.Free; end; end; end; function TServerMethods1.ReverseString(Value: string): string; begin Result := System.StrUtils.ReverseString(Value); end; end. Status API Training Shop Blog About Pricing © 2015 GitHub, Inc. Terms Privacy Security Contact Help
Delphi DataSet與JSON互轉
DataSetConverter4Delphi
https://github.com/ezequieljuliano/DataSetConverter4Delphi
用webmodule返回
https://community.embarcadero.com/blogs/entry/display-json-base64-images-created-in-c-builder-using-ext-js-in-browser
void __fastcall TWebModule_main::WebModule_mainWebActionItem1Action(TObject *Sender,
TWebRequest *Request, TWebResponse *Response, bool &Handled)
function TServerConnMonitorMethodsClient.ReverseString(Value: string): string; begin if FReverseStringCommand = nil then begin FReverseStringCommand := FDBXConnection.CreateCommand; FReverseStringCommand.CommandType := TDBXCommandTypes.DSServerMethod; FReverseStringCommand.Text := 'TServerConnMonitorMethods.ReverseString'; FReverseStringCommand.Prepare; end; FReverseStringCommand.Parameters[0].Value.SetWideString(Value); FReverseStringCommand.ExecuteUpdate; Result := FReverseStringCommand.Parameters[1].Value.GetWideString; end;