Delphi и SQLite. Альтернатива хранимым процедурам

от автора

SQLite во многих случаях является удобным, незаменимым инструментом. Я уже не могу себе представить — как мы все жили без него. Тем не менее, есть некоторые неудобства при его использовании, связанные с тем, что это легкая встраиваемая СУБД.

Самое большое неудобство для меня, как Delphi-разработчика — отсутствие хранимых процедур. Я очень не люблю смешивать Delphi-код и SQL-скрипты. Это делает код намного менее читабельным, и затрудняет его поддержку. Следовательно, нужно как-то разнести код Delphi и тексты SQL-скриптов.

Предлагаю свой вариант решения проблемы

  • Выносим весь SQL-код в отдельный тестовый файл ресурсов, подключенный к проекту.

  • Запросы в SQL-файле разделяем маркерами начала с идентификаторами и маркерами конца. В моём случае синтаксис маркера начала — //SQL ИмяПроцедуры. Маркер конца — GO.

  • Создаем класс — менеджер SQL-запросов. При загрузке приложения он читает SQL-файл из ресурсов и составляет из него список хранимых процедур с уникальными именами-идентификаторами.

  • В процессе работы приложения мендежер извлекает текст SQL-запроса по его идентификатору для последующей его передачи на выполнение.

Главная идея — простота и легкость использования, подобная вызову хранимых процедур и удобство при создании и модификации SQL-запросов

Код юнита менеджера запросов:

unit uSqlList;  interface  uses System.Classes, Winapi.Windows, System.SysUtils,   System.Generics.Collections;  type   TSqlList = class(TObjectDictionary<string, TStrings>)   const     SCRIPTS_RCNAME = 'SqlList';   private     function GetScripts(const AName: string): TStrings;     procedure FillList;     function GetItem(const AKey: string): string;   public     constructor Create;   public     property Sql[const Key: string]: string read GetItem; default;   end;  var   SqlList: TSqlList;  implementation  function GetStringResource(const AName: string): string; var   LResource: TResourceStream; begin   LResource := TResourceStream.Create(hInstance, AName, RT_RCDATA);    with TStringList.Create do     try       LoadFromStream(LResource);       Result := Text;     finally       Free;       LResource.Free;     end; end;  { TScriptList }  constructor TSqlList.Create; begin   inherited Create([doOwnsValues]);   FillList; end;  procedure TSqlList.FillList; var   LScripts: TStrings;   I: Integer;   S, LKey: string;   LStarted: Boolean;   LSql: TStrings; begin   LScripts := GetScripts(SCRIPTS_RCNAME);   try     LStarted := False;     LSql := nil;     for I := 0 to LScripts.Count - 1 do     begin       S := LScripts[I];       if LStarted then       begin         if S = 'GO' then         begin           LStarted := False;           Continue;         end         else if not S.StartsWith('//') then           LSql.Add(S);       end       else       begin         LStarted := S.StartsWith('//SQL ');         if LStarted then         begin           LKey := S.Substring(6);           LSql := TStringList.Create;           Add(LKey, LSql);         end;         Continue;       end;     end;   finally     LScripts.Free;   end; end;  function TSqlList.GetItem(const AKey: string): string; begin   Result := Items[AKey].Text; end;  function TSqlList.GetScripts(const AName: string): TStrings; begin   Result := TStringList.Create;   try     Result.Text := GetStringResource(AName);   except     FreeAndNil(Result);     raise;   end; end;  initialization  SqlList := TSqlList.Create;  finalization  FreeAndNil(SqlList);  end.

Пример содержимого файла SQL-скриптов:

//SQL GetOrder SELECT * FROM Orders WHERE ID = :ID GO  //SQL DeleteOpenedOrders DELETE FROM Orders WHERE Closed = 0 GO

Подключение файла скриптов к проекту:

{$R 'SqlList.res' '..\Common\DataBase\SqlList.rc'}

Использование с компонентом TFDConnection:

  Connection.ExecSQL(SqlList['GetOrder'], ['123']);

Собственно, это всё. Использую данное решение уже в нескольких проектах и мне оно кажется очень удобным. Буду благодарен за советы и замечания. Рад, если мой посто кому-то будет полезен!

ссылка на оригинал статьи https://habr.com/ru/post/563690/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *