2012-01-23 2 views
3

Я играю с реализацией SQLite Synopse, но я придерживаюсь следующей части кода. В конструкторе формы я создаю модель базы данных, где есть две таблицы Task и Comment и одна таблица TaskComments с соотношением 1: N для комментариев задачи. Я могу добавить строки в таблицу TaskComments (событие Button1.OnClick добавить одну задачу и два комментария для нее), но я не знаю, как получить комментарии для этой задачи.
Synopse SQLite select rows for отношения 1: N

Может ли кто-нибудь предложить мне, как получить N строк для определенной строки (как получить комментарии к задаче в данном случае)?

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. 

Большое спасибо

+0

@mjn - Если бы этот тег существовал :-) Но это несовершеннолетний, о чем я беспокоюсь. –

+1

mormot.create() :) – mjn

+0

Используйте форум Synopse, который очень эффективен. – philnext

ответ

7

Вы должны выложили это на официальном форуме mORMot, который не спит, как и другие сурки в эти дни ... но это очень приятно видеть такой вопрос в SO!

Прежде всего, некоторые общие замечания:

  • Вы должны лучше использовать UTF8ToString вместо UTF8ToWideString функции;
  • Если вы создаете экземпляры объектов, вам лучше использовать вложенные блоки try..finally: например. если конструктор TComment.CreateAndPrepare терпит неудачу и вызывает исключение, вы никогда не достигнете кода FreeAndNil(Task), поэтому вы будете утечка памяти;
  • Позаботьтесь, используя FreeAndNil() очень опасно в те дни в сообществе Delphi - вы можете быть анафеме!
  • Основные FSQLModel должны быть опубликованы и жить в течение всего времени базы данных;
  • 3D-параметр TSQLRestClientDB должен быть равен нулю;
  • Для освобождения памяти требуется FormDestroy, но здесь это не главное.

О коде, на самом деле, как указано в документации, TSQLRecordMany подклассу должен, по крайней мере, два опубликованных свойств, названных Source и Dest, по соглашению:

  • по умолчанию должны быть созданы только два поля TSQLRecord (т.е. INTEGER), с именем «Источник» и «Dest», первый указатель на запись источника (один с опубликованным свойством TSQLRecordMany), а второй - адресом назначения ord ...
  • во всех случаях, в Лите два «Источник» и опубликованные свойства «Dest» должны быть объявлены как TSQLRecord детей в любом TSQLRecordMany потомке , потому что они всегда будут необходимы для «многих ко многим» отношений

Затем он должен работать, как ожидалось:

TTaskComments = class(TSQLRecordMany) 
    private 
    FSource: TTask; 
    FDest: TComment; 
    published 
    property Source: TTask read FSource; 
    property Dest: TComment read FDest; 
    end; 

и обратите внимание, что метод FillMany() только заполняет Source и Dest как идентификаторы, так что вы не можете здесь непосредственно получите Source.TaskName или Dest.CommentText. Вам придется использовать вместо этого, например, метод DestGetJoined для извлечения необходимых полей. См. Документацию об этом методе или прочитайте процедуру TestMany в методе TTestSQLite3Engine._TSQLRestClientDB блока SQLite3.pas.

Вы также можете взглянуть на новую функцию «Автоматический JOIN-запрос» (в соединительной линии 1.16): она сделает ваш запрос только для вас. См. this article.

+1

+1 «вы можете быть анафеме» –

+0

Спасибо большое за все советы! Я просмотрю свой код, и я с нетерпением жду новых функций текущей версии. –

Смежные вопросы