首先我们来看FireDAC的数据对象,看看它的内部保存的方式。
FireDAC数据保存,大致流程如下:
“TFDDataSet.InternalSaveToStorage”
-》“TFDDatSManager.InternalSaveToStorage”
-》“TFDDatSTable.InternalSaveToStorage”
– 》“TFDDatSColumnList.InternalSaveToStorage”
– 》“TFDDatSTableRowList.InternalSaveToStorage”
根据传入不同的Storage来获取不同的格式的Storage,通过Storage的Write和Read相关方法,来保存数据所有的数据和参数。
那我们用QJson,也是可以借鉴上面的方式来保存数据。
我的方法如下:
procedure SaveToJSON(Node: TQJson);
procedure WriteValue(ANode: TQJson; const APropName: String; APropIndex: Word;
ADataType: TFDDataType; ABuff: Pointer; ALen: LongWord); overload;
const
CDateTimeISO8601Format: String = '%.4d%.2d%.2dT%.2d%.2d%.2d';
CTimeFormat: String = '%.2d%.2d%.2d';
CDateFormat: String = '%.4d%.2d%.2d';
var
sVal: String;
sSign: String;
iLen: Integer;
y, mo, d, h, mi, se, ms: Word;
dt: TDateTime;
pTS: PSQLTimeStamp;
pInt: PFDSQLTimeInterval;
rTS: TSQLTimeStamp;
pDest: PChar;
aFixed: array [0 .. C_FD_MaxFixedSize] of Char;
begin
if ABuff = nil then
Exit;
pDest := @aFixed[0];
iLen := SizeOf(aFixed) div SizeOf(Char);
case ADataType of
dtObject,
dtRowSetRef,
dtCursorRef,
dtRowRef,
dtArrayRef,
dtParentRowRef:
;
dtWideMemo,
dtWideHMemo,
dtXML,
dtWideString:
ANode.Add(APropName, PWideChar(ABuff), jdtString);
dtMemo,
dtHMemo,
dtAnsiString:
begin
pDest := nil;
iLen := FQuery.Encoder.Decode(ABuff, ALen, Pointer(pDest), ecUTF16, ecANSI);
ANode.Add(APropName, pDest, jdtString);
end;
dtByteString,
dtBlob,
dtHBlob,
dtHBFile:
ANode.Add(APropName, FDBin2Hex(ABuff, ALen), jdtString);
dtBoolean:
ANode.Add(APropName, PWord(ABuff)^ <> 0);
dtSByte:
begin
FDInt2Str(ABuff, SizeOf(ShortInt), pDest, iLen, False, 0);
ANode.Add(APropName, pDest, jdtInteger);
end;
dtInt16:
begin
FDInt2Str(ABuff, SizeOf(SmallInt), pDest, iLen, False, 0);
ANode.Add(APropName, pDest, jdtInteger);
end;
dtInt32:
begin
FDInt2Str(ABuff, SizeOf(Integer), pDest, iLen, False, 0);
ANode.Add(APropName, pDest, jdtInteger);
end;
dtInt64:
begin
FDInt2Str(ABuff, SizeOf(Int64), pDest, iLen, False, 0);
ANode.Add(APropName, pDest, jdtInteger);
end;
dtByte:
begin
FDInt2Str(ABuff, SizeOf(Byte), pDest, iLen, True, 0);
ANode.Add(APropName, pDest, jdtInteger);
end;
dtUInt16:
begin
FDInt2Str(ABuff, SizeOf(Word), pDest, iLen, True, 0);
ANode.Add(APropName, pDest, jdtInteger);
end;
dtUInt32:
begin
FDInt2Str(ABuff, SizeOf(LongWord), pDest, iLen, True, 0);
ANode.Add(APropName, pDest, jdtInteger);
end;
dtUInt64:
begin
FDInt2Str(ABuff, SizeOf(UInt64), pDest, iLen, True, 0);
ANode.Add(APropName, pDest, jdtInteger);
end;
dtSingle:
ANode.Add(APropName, FDFloat2Str(PSingle(ABuff)^, '.'), jdtFloat);
dtDouble:
ANode.Add(APropName, FDFloat2Str(PDouble(ABuff)^, '.'), jdtFloat);
dtExtended:
ANode.Add(APropName, FDFloat2Str(PExtended(ABuff)^, '.'), jdtFloat);
dtCurrency:
ANode.Add(APropName, FDFloat2Str(PCurrency(ABuff)^, '.'), jdtFloat);
dtBCD,
dtFmtBCD:
begin
FDBCD2Str(pDest, iLen, PBcd(ABuff)^, '.');
ANode.Add(APropName, pDest, jdtFloat);
end;
dtDateTime:
begin
dt := TimeStampToDateTime(MSecsToTimeStamp(PDateTimeRec(ABuff)^.DateTime));
DecodeDate(dt, y, mo, d);
DecodeTime(dt, h, mi, se, ms);
iLen := WideFormatBuf(pDest^, iLen, Pointer(CDateTimeISO8601Format)^,
Length(CDateTimeISO8601Format), [y, mo, d, h, mi, se], FormatSettings);
ANode.Add(APropName, pDest, jdtDateTime);
end;
dtDateTimeStamp:
begin
pTS := PSQLTimeStamp(ABuff);
iLen := WideFormatBuf(pDest^, iLen, Pointer(CDateTimeISO8601Format)^,
Length(CDateTimeISO8601Format), [pTS^.Year, pTS^.Month, pTS^.Day, pTS^.Hour,
pTS^.Minute, pTS^.Second], FormatSettings);
ANode.Add(APropName, pDest, jdtString);
end;
dtTimeIntervalFull,
dtTimeIntervalYM,
dtTimeIntervalDS:
begin
pInt := PFDSQLTimeInterval(ABuff);
if pInt^.Sign < 0 then
sSign := '-'
else
sSign := '';
case pInt^.Kind of
itYear:
sVal := Format('%sP%uY', [sSign, pInt^.Years]);
itMonth:
sVal := Format('%sP%uM', [sSign, pInt^.Months]);
itDay:
sVal := Format('%sP%uD', [sSign, pInt^.Days]);
itHour:
sVal := Format('%sT%uH', [sSign, pInt^.Hours]);
itMinute:
sVal := Format('%sT%uM', [sSign, pInt^.Minutes]);
itSecond:
sVal := Format('%sT%uS%uF', [sSign, pInt^.Seconds, pInt^.Fractions]);
itYear2Month:
sVal := Format('%sP%uY%uM', [sSign, pInt^.Years, pInt^.Months]);
itDay2Hour:
sVal := Format('%sP%uDT%uH', [sSign, pInt^.Days, pInt^.Hours]);
itDay2Minute:
sVal := Format('%sP%uDT%uH%uM', [sSign, pInt^.Days, pInt^.Hours, pInt^.Minutes]);
itDay2Second:
sVal := Format('%sP%uDT%uH%uM%uS%uF', [sSign, pInt^.Days, pInt^.Hours, pInt^.Minutes, pInt^.Seconds, pInt^.Fractions]);
itHour2Minute:
sVal := Format('%sT%uH%uM', [sSign, pInt^.Hours, pInt^.Minutes]);
itHour2Second:
sVal := Format('%sT%uH%uM%uS%uF', [sSign, pInt^.Hours, pInt^.Minutes, pInt^.Seconds, pInt^.Fractions]);
itMinute2Second:
sVal := Format('%sT%uM%uS%uF', [sSign, pInt^.Minutes, pInt^.Seconds, pInt^.Fractions]);
end;
ANode.Add(APropName, sVal, jdtString);
end;
dtTime:
begin
rTS := FDTime2SQLTimeStamp(PLongint(ABuff)^);
iLen := WideFormatBuf(pDest^, iLen, Pointer(CTimeFormat)^,
Length(CTimeFormat), [rTS.Hour, rTS.Minute, rTS.Second], FormatSettings);
ANode.Add(APropName, pDest, jdtDateTime);
end;
dtDate:
begin
rTS := FDDate2SQLTimeStamp(PLongint(ABuff)^);
iLen := WideFormatBuf(pDest^, iLen, Pointer(CDateFormat)^,
Length(CDateFormat), [rTS.Year, rTS.Month, rTS.Day], FormatSettings);
ANode.Add(APropName, pDest, jdtDateTime);
end;
dtGUID:
ANode.Add(APropName, GUIDToString(PGUID(ABuff)^), jdtString);
end;
end;
var
lNode, lRowsNode, lRowNode: TQJson;
i, j: Integer;
oRows: TFDDatSTableRowList;
oRow: TFDDatSRow;
oCols: TFDDatSColumnList;
oCol: TFDDatSColumn;
pBuff: Pointer;
iLen: LongWord;
begin
if FQuery <> nil then
begin
oRows := FQuery.Table.Rows;
oCols := FQuery.Table.Columns;
lNode := Node.Add('data', jdtObject);
lRowsNode := lNode.AddArray('rows');
for i := 0 to oRows.Count - 1 do
begin
oRow := oRows.ItemsI[i];
lRowNode := lRowsNode.Add();
for j := 0 to oCols.Count - 1 do
begin
pBuff := nil;
iLen := 0;
oCol := oCols.ItemsI[j];
oRow.GetData(j, rvDefault, pBuff, 0, iLen, False);
WriteValue(lRowNode, oCol.Name, j, oCol.DataType, pBuff, iLen);
end;
end;
end;
end;
这样的保存方式,滚动的效率上比。你们自已测试一下就知道了,我不说了。
我做过对比,这样保存效率的差距是在QJson的SaveToStream这一步。期待swish的QDAC3.0。
