首先我们来看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。