Если вы действительно собираетесь использовать класс базы данных, как показано, рассмотрите возможность записей содержать функции в D2007 и более поздних версиях.
Например, ваш пример будет:
type
TUserTable = record
TableName : String;
Username : String;
Password : String;
function sqlGetUserName(where:string=''):string;
end;
const
UserTable : TUserTable =
(
TableName : 'users';
Username : 'Username';
Password : 'Password';
);
function TUserTable.sqlGetUserName(where:string=''): string;
begin
if where='' then result := Format('SELECT %s from %s', [userName, tableName])
else result := Format('SELECT %s from %s where %s', [userName, tableName, where]);
end;
, что позволяет:
query.SQL.add(userTable.sqlGetUserName);
или
query.SQL.add(userTable.sqlGetUserName(Format('%s=%s', [userTable.userName,'BOB']));
Я действительно не рекомендую использовать SQL напрямую, как вы иллюстрировали. На мой взгляд, вы никогда не должны иметь прямых вызовов SQL для таблиц. Это создает большую взаимосвязь между пользовательским интерфейсом и базой данных (которая не должна существовать) и не позволяет вам обеспечить высокий уровень безопасности при прямой модификации таблицы.
Я бы обернул все в хранимые процедуры и имел бы класс интерфейса БД, который инкапсулирует весь код базы данных в модуль данных. Вы по-прежнему можете использовать прямые ссылки на компоненты, учитывающие данные, из модуля данных, вам просто нужно предварять ссылки именем DM.
Например, если вы создали класс вроде:
type
TDBInterface = class
private
function q(s:string):string; //just returns a SQL quoted string
public
procedure addUser(userName:string; password:string);
procedure getUser(userName:string);
procedure delUser(userName:string);
function testUser:boolean;
procedure testAllDataSets;
end;
function TDBInterface.q(s:string):string;
begin
result:=''''+s+'''';
end;
procedure TDBInterface.addUser(userName:string; password:string);
begin
cmd.CommandText:=Format( 'if (select count(userName) from users where userName=%s)=0 '+
'insert into users (userName, password) values (%s,%s) '+
'else '+
'update users set userName=%s, password=%s where userName=%s',
[q(userName), q(userName), q(password), q(userName), q(password), q(userName)]);
cmd.Execute;
end;
procedure TDBInterface.getUser(userName:string);
begin
qry.SQL.Add(Format('select * from users where userName=%s', [q(userName)]));
qry.Active:=true;
end;
procedure TDBInterface.delUser(userName:string);
begin
cmd.CommandText:=Format('delete from users where userName=%s',[userName]);
cmd.Execute;
end;
procedure TDBInterface.testAllDataSets;
begin
assert(testUser);
end;
function TDBInterface.testUser: boolean;
begin
result:=false;
addUser('99TEST99','just a test');
getUser('99TEST99');
if qry.IsEmpty then exit;
if qry.FieldByName('userName').value<>'99TEST99' then
exit;
delUser('99TEST99');
if qry.IsEmpty then
result:=true;
end;
Теперь у вас есть возможность выполнить некоторую форму модульного тестирования на вашем интерфейсе данных, вы удалили SQL из пользовательского интерфейса, и все идет вверх. У вас все еще много уродливого SQL в коде интерфейса, поэтому перенесите его на хранимые процедуры и вы получите:
type
TDBInterface = class
public
procedure addUser(userName:string; password:string);
procedure getUser(userName:string);
procedure delUser(userName:string);
function testUser:boolean;
procedure testAllDataSets;
end;
procedure TDBInterface.addUser(userName:string; password:string);
begin
cmd.CommandText:='usp_addUser;1';
cmd.Parameters.Refresh;
cmd.Parameters.ParamByName('@userName').Value:=userName;
cmd.Parameters.ParamByName('@password').Value:=password;
cmd.Execute;
cmd.Execute;
end;
procedure TDBInterface.getUser(userName:string);
begin
sproc.Parameters.ParamByName('@userName').Value:=userName;
sproc.Active:=true;
end;
procedure TDBInterface.delUser(userName:string);
begin
cmd.CommandText:='usp_delUser;1';
cmd.Parameters.Refresh;
cmd.Parameters.ParamByName('@userName').Value:=userName;
cmd.Execute;
end;
Теперь вы можете переместить некоторые из этих функций в поток ADO, и пользовательский интерфейс не будет знать, что добавление или удаление пользователей происходит в отдельном процессе. Имейте в виду, что это очень простые операции, поэтому, если вы хотите сделать такие удобные вещи, как уведомление родителя о завершении процесса (например, для обновления списка пользователей после добавления / удаления / обновления), вам нужно кодировать его в потоке модель.
Кстати, сохраненный процесс добавления кода выглядит следующим образом:
create procedure [dbo].[usp_addUser](@userName varchar(20), @password varchar(20)) as
if (select count(userName) from users where userName=@userName)=0
insert into users (userName, password) values (@userName,@password)
else
update users set userName=@userName, password=@password where userName=@userName
Кроме того, небольшой отказ от ответственности: этот пост довольно длинный, и, хотя я пытался проверить большую его часть, я, возможно, что-то пропустил где-то.