I'm playing with the Synopse's SQLite implementation, but I'm stucked with the following piece of code. In the form constructor I create a database model where there are two tables Task and Comment and one table TaskComments with relationship 1:N for task comments. I can add the rows into the TaskComments table (Button1.OnClick event add one task and two comments for it) but I don't know how to get comments for this task back.
Can anyone suggest me how to get the N rows for a certain row (how to get comments for the task, in this case) ?
unit SynopseSQLiteTestUnit;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, SynCommons, SQLite3, SQLite3Commons, StdCtrls;
type
  TTask = class(TSQLRecord)
  private
    FTaskName: RawUTF8;
    FTaskCreated: TDateTime;
  published
    property TaskName: RawUTF8 read FTaskName write FTaskName;
    property TaskCreated: TDateTime read FTaskCreated write FTaskCreated;
  end;
  TComment = class(TSQLRecord)
  private
    FCommentText: RawUTF8;
    FCommentCreated: TDateTime;
  published
    property CommentText: RawUTF8 read FCommentText write FCommentText;
    property CommentCreated: TDateTime read FCommentCreated write FCommentCreated;
  end;
  TTaskComments = class(TSQLRecordMany)
  private
    FTask: TTask;
    FComment: TComment;
  published
    property Task: TTask read FTask;
    property Comment: TComment read FComment;
  end;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    Memo2: TMemo;
    Memo3: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    FDatabase: TSQLRestClientURI;
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
  SQLModel: TSQLModel;
begin
  SQLModel := TSQLModel.Create([
    TTask,
    TComment,
    TTaskComments
  ]);
  FDatabase := TSQLRestClientDB.Create(SQLModel, SQLModel, ChangeFileExt(Application.ExeName,'.db3'), TSQLRestServerDB);
  TSQLRestClientDB(FDatabase).Server.CreateMissingTables(0);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  Task: TTask;
  TaskID: Integer;
  Comment: TComment;
  CommentID: Integer;
  TaskComments: TTaskComments;
begin
  Task := TTask.Create;
  Comment := TComment.Create;
  TaskComments := TTaskComments.Create;
  try
    Task.TaskName := StringToUTF8('Task Name');
    Task.TaskCreated := Now;
    TaskID := FDatabase.Add(Task, True);
    Comment.CommentText := StringToUTF8('Comment Text 1');
    Comment.CommentCreated := Now;
    CommentID := FDatabase.Add(Comment, True);
    TaskComments.ManyAdd(FDatabase, TaskID, CommentID);
    Comment.CommentText := StringToUTF8('Comment Text 2');
    Comment.CommentCreated := Now;
    CommentID := FDatabase.Add(Comment, True);
    TaskComments.ManyAdd(FDatabase, TaskID, CommentID, True);
  finally
    FreeAndNil(Task);
    FreeAndNil(Comment);
    FreeAndNil(TaskComments);
  end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  Task: TTask;
  Comment: TComment;
  TaskComments: TTaskComments;
begin
  Memo1.Clear;
  Memo2.Clear;
  Memo3.Clear;
  // here I want to select task with ID = 1, that's fine
  Task := TTask.CreateAndFillPrepare(FDatabase, 'ID = 1');
  // here I want to select all comments, that's fine
  Comment := TComment.CreateAndFillPrepare(FDatabase, '');
  // here I want to create the task comments, ok
  TaskComments := TTaskComments.Create;
  try
    // here I'm filling the memo boxes with the task and all comments, ok
    while Task.FillOne do
      Memo1.Lines.Add(UTF8ToWideString(Task.TaskName));
    while Comment.FillOne do
      Memo2.Lines.Add(UTF8ToWideString(Comment.CommentText));
    // here I'm trying to get all comments for task with ID = 1
    // but the FillOne function returns always False, what means, that
    // I don't get any row fetched
    TaskComments.FillMany(FDatabase, 1);
    while TaskComments.FillOne do
      Memo3.Lines.Add(UTF8ToWideString(TaskComments.Task.TaskName) + '; ' + UTF8ToWideString(TaskComments.Comment.CommentText));
  finally
    FreeAndNil(Task);
    FreeAndNil(Comment);
    FreeAndNil(TaskComments);
  end;
end;
end.
Many thanks
                        
You should have posted this on the official mORMot forum, which is not sleeping like other marmots these days... but it's very nice seeing such a question in SO!
First of all, some general notes:
UTF8ToStringinstead ofUTF8ToWideStringfunction;try..finallyblocks: e.g. if theTComment.CreateAndPrepareconstructor fails and raise an exception, you will never reach theFreeAndNil(Task)code, so you'll leak memory;FreeAndNil()is very dangerous those days in the Delphi community - you may be anathemized!FSQLModelshould be made public, and live during all database time;TSQLRestClientDB3d parameter (server model) should be nil;FormDestroyis needed to release the memory, but it is not the main point here;About your code, in fact, as stated by the documentation, a
TSQLRecordManysub-class shall have at least two published properties, namedSourceandDest, by convention:Then, it should work as expected:
And note that the
FillMany()method only fills theSourceandDestas IDs, so you can't here directly getSource.TaskNameorDest.CommentText. You'll have to use instead e.g. theDestGetJoinedmethod to retrieve the needed fields. See the documentation about that method, or read theTestManyprocedure inTTestSQLite3Engine._TSQLRestClientDBmethod of theSQLite3.pasunit.You may also take a look at the "Automatic JOIN query" new feature (in the 1.16 trunk): it will make your query just for you. See this article.