Синопсировать SQLite выбрать строки для отношения 1: N - PullRequest
3 голосов
/ 23 января 2012

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

Может кто-нибудь подсказать, как получить 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.

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

1 Ответ

7 голосов
/ 23 января 2012

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

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

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

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

  • по умолчанию должны быть созданы только два поля TSQLRecord (т.е. INTEGER), с именами «Source» и «Dest», первый указывает на исходную запись (тот, с опубликованным свойством TSQLRecordMany) и второй записью назначения ...
  • во всех случаях по крайней мере два опубликованных свойства 'Source' и '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): она сделает ваш запрос только для вас. См. эту статью .

...