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']);
Собственно, это всё. Использую данное решение уже в нескольких проектах и мне оно кажется очень удобным. Буду благодарен за советы и замечания. Рад, если мой посто кому-то будет полезен!
Sm1le291
На Delphi нету своей ORM? Что за велосипед?
kai3341
На сегодня я знаю только одну ORM, которая действительно умеет в SQL. Остальное может только в `Hello, World!`.
Однако в примерах простейший SQL, который действительно лучше генерить с помощью ORM.
Sm1le291
Что в вашем понятии умеет в SQL и какие есть аргументы против orm?
kai3341
На этот вопрос я уже ответил в приведённой выше ссылке. Прошу обратить внимание, что в центре статьи вовсе не производительность query builder, а широкие возможности в генерации SQL, включая макросы и интроспекцию
ahdenchik
Посмотрите как она ищет места в SQL-запросе куда нужно добавить долларовые ($1) аргументы - она просто делает поиск вместо синтаксического разбора
HemulGM
Есть как минимум 3 ORM более известных для Delphi. И более десятка ORM, написанных случайными разработчиками и выложенных на гите.
Только не всегда ORM — это оптимальное решение. Т.к. в таком случае приходится оперировать объектами, что во многих случаях не нужно.
Здесь, возможно, нужно иметь некоторый опыт в разработке на Delphi, чтобы понять. Крупные проекты работают совсем иначе, чем «программы» на других языках. Так, когда вообще не нужно описывать структуру таблиц, но иметь возможность добавлять, изменять и удалять данные и даже если структура таблицы изменяется.
Sm1le291
А чем крупные проекты на Делфи отличаются от крупных проектов на других языках? В частности использую NHibernate и entity framework на проектах разного уровня на си шарпе и проблем никогда не было. Что такого особенного с Делфи? Мне кажется sql запросы в итоге выполняются на базе данных и не важно на каком языке запрос был написан изначально
HemulGM
Многие крупные проекты в делфи работают без бэкенда, напрямую используя подключение к БД. А ORM, и вообще CRUD и прочее появились с веб-проектами.
Имея прямое подключение к БД имеется прямой доступ к таблицам и данным. В делфи имеются огромные возможности для работы с БД в дизайнтайме (в режиме дизайна проекта). Для того, чтобы написать крупный проект с БД можно обойтись без вообще написания кода. Достаточно положить на форму нужные таблицы из списка компонент. Выбрать подключение, создать либо компонент для получения данных из таблицы (напрямую), либо компонент, в котором мы пишем запрос (любой сложности). Указать куда отображать содержимое. Если имеется прямое подключение к таблице, мы можем манипулировать ею как хотим (CRUD). Если мы используем свой запрос, то если он не сложный, то мы тоже имеем сразу возможность манипулировать данными, если запрос сложный, то требуется, либо описать запросы редактирования, либо редактировать вручную. За исключением последнего, мы вообще ещё не прикасались к редактору кода.
qw1
ORM без GC та ещё боль.
HemulGM
Дельфисты привыкшие к подчистке за собой. Это даёт свои плюшки. Да и это можно решить разными средствами по типу TComponent и удалением лишь корневого элемента.