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:
UTF8ToString
instead ofUTF8ToWideString
function;try..finally
blocks: e.g. if theTComment.CreateAndPrepare
constructor 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!FSQLModel
should be made public, and live during all database time;TSQLRestClientDB
3d parameter (server model) should be nil;FormDestroy
is needed to release the memory, but it is not the main point here;About your code, in fact, as stated by the documentation, a
TSQLRecordMany
sub-class shall have at least two published properties, namedSource
andDest
, by convention:Then, it should work as expected:
And note that the
FillMany()
method only fills theSource
andDest
as IDs, so you can't here directly getSource.TaskName
orDest.CommentText
. You'll have to use instead e.g. theDestGetJoined
method to retrieve the needed fields. See the documentation about that method, or read theTestMany
procedure inTTestSQLite3Engine._TSQLRestClientDB
method of theSQLite3.pas
unit.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.