実際に図のようにデータベース クラスを使用する場合は、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 呼び出しを行うべきではありません。これにより、UI とデータベース (存在してはならない) の間に多くの結合が導入され、テーブルの直接変更に高レベルのセキュリティを設定できなくなります。
すべてをストアド プロシージャにラップし、すべてのデータベース コードをデータ モジュールにカプセル化する DB インターフェイス クラスを用意します。データ モジュールからデータベース対応コンポーネントへの直接リンクを引き続き使用できます。リンクの前に 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;
これで、データ インターフェイスで何らかの形式の単体テストを実行できるようになりました。UI から 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 スレッドに移動すると、UI はユーザーの追加または削除が別のプロセスで行われることを認識しなくなります。これらは非常に単純な操作なので、プロセスが完了したときに親に通知するなどの便利なことをしたい場合 (たとえば、追加/削除/更新が発生した後にユーザー リストを更新するため)、それをスレッドにコーディングする必要があります。モデル。
ところで、追加コードのストアド プロシージャは次のようになります。
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
また、ちょっとした免責事項: この投稿はかなり長いので、ほとんどを確認しようとしましたが、どこかで何かを見落としている可能性があります。