#1 2011-07-15 11:40:02

JCano
Member
From: Spain
Registered: 2011-07-15
Posts: 3

Simple TSQLRecord descendent property

I want to compare two sets of files. A file (filename) may appear in just one set or in both of them. And, in this case, they may have the same size an modification date or not.
So, for every single file I have the following structure:

  TSQLMyFileInfo = class(TSQLRecord)
  private
    FMyFileDate: TDateTime;
    FMyFileSize: Int64;
    procedure SetMyFileDate(const Value: TDateTime);
    procedure SetMyFileSize(const Value: Int64);
  published
    property MyFileDate: TDateTime read FMyFileDate write SetMyFileDate;
    property MyFileSize: Int64 read FMyFileSize write SetMyFileSize;
  end;

  TSQLMyFile = class(TSQLRecord)
  private
    FSecondOne: TSQLMyFileInfo;
    FFirstOne: TSQLMyFileInfo;
    FMyFileName: RawUTF8;
    FMyFileInfo: TSQLMyFileInfo;
    procedure SetFirstOne(const Value: TSQLMyFileInfo);
    procedure SetMyFileName(const Value: RawUTF8);
    procedure SetSecondOne(const Value: TSQLMyFileInfo);
    procedure SetMyFileInfo(const Value: TSQLMyFileInfo);
  published
    property MyFileName: RawUTF8 read FMyFileName write SetMyFileName;
    property FirstOne: TSQLMyFileInfo read FFirstOne write SetFirstOne;
    property SecondOne: TSQLMyFileInfo read FSecondOne write SetSecondOne;
    property MyFileInfo: TSQLMyFileInfo read FMyFileInfo write SetMyFileInfo;
  end;

To save the "MyFile" object I do the following, after filling its properties:

          MyDataBase.Add(MyFile.FirstOne, True);
          MyDataBase.Add(MyFile.SecondOne, True);
          MyDataBase.Add(MyFile, True);

But when I look into the table ("MyFile") I see it has saved the address of the objects (FirstOne, SecondOne) instead of their ids.

Could someone please tell me what am I doing wrong?

Thanks in advance

Offline

#2 2011-07-15 11:57:39

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Simple TSQLRecord descendent property

MyFile.FirstOne and MyFile.SecondOne should be IDs, so you can't use those properties as TSQLMyFileInfo instances.

As stated by the documentation, TSQLRecord published properties do not contain an instance of the TSQLRecord class. It will instead contain pointer(RowID), and will be stored as an INTEGER in the database.

So do not use directly such published properties, like a regular class instance: you'll have an access violation.

When creating such records, use temporary instances for each detail object, as such:

var One, Two: TSQLMyFileInfo;
     MyFile: TSQLMyFile;
begin
  One := TSQLMyFileInfo.Create;
  Two := TSQLMyFileInfo.Create;
  MyFile := TSQLMyFile.Create;
  try
    One.MyFileDate := ....
    One.MyFileSize := ...
    MyFile.FirstOne := TSQLMyFileInfo(MyDataBase.Add(One,True)); // add One and store ID in MyFile.FirstOne
    Two.MyFileDate := ....
    Two.MyFileSize := ...
    MyFile.SecondOne:= TSQLMyFileInfo(MyDataBase.Add(Two,True)); // add Two and store ID in MyFile.SecondOne
    MyDataBase.Add(MyFile);
  finally
     MyFile.Free;
     Two.Free;
     One.Free;
  end;
end;

When accessing the detail objects, don't access directly to FirstOne or SecondOne properties (there are not class instances, but IDs), then use instead the TSQLRecord. Create(aClient: TSQLRest; aPublishedRecord: TSQLRecord: ForUpdate: boolean=false) overloaded constructor, as such:

var One: TSQLMyFileInfo;
    MyFile: TSQLMyFile;
begin                 
  MyFile := TSQLMyFile.Create(Client,aMyFileID); 
  try
    // here MyFile.FirstOne.MyFileDate will trigger an access violation
    One := TSQLMyFileInfo.Create(Client,MyFile.FirstOne);
    try
      // here you can access One.MyFileDate or One.MyFileSize
    finally
      One.Free;
    end;
  finally
    MyFile.Free;
  end;
end;

Or with a with statement:

with TSQLMyFileInfo.Create(Client,MyFile.FirstOne) do
    try
      // here you can access MyFileDate or MyFileSize
    finally
      Free;
    end;

Up to now, there is no Lazy Loading feature in our ORM, for the TSQLRecord classes. This could sound like a limitation, but it allows to manage exactly the data to be retrieved from the server in your code, and maintain bandwidth and memory use as low as possible. Lazy loading is available by defining TPersistent, TCollection or dynamic arrays in the TSQLRecord published fields: as we'll state in the following paragraphs, the content of those fields are stored and retrieved together with the other simple published fields (e.g. integer, RawUTF8...).

The only case when some class instance are automatically created is for TSQLRecordMany published properties (see the documentation).

Offline

#3 2011-07-18 06:38:50

JCano
Member
From: Spain
Registered: 2011-07-15
Posts: 3

Re: Simple TSQLRecord descendent property

Thanks a lot for answering so quickly.

I had already read "TSQLRecord published properties do not contain an instance of the TSQLRecord class" in the SAD but I did not fully understand it.

I am going to try it right now.

Offline

#4 2011-07-18 06:52:58

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Simple TSQLRecord descendent property

I've updated the documentation with my answer above.
It was not clear enough. wink

In fact, the answer above is a copy & paste of the updated paragraph of the documentation.
Thanks for the feedback and interest.

Offline

Board footer

Powered by FluxBB