2009-03-05 10 views
3

Ich fange an, meine erste Delphi-Anwendung zu schreiben, die eine Verbindung zu einer SQL-Datenbank (MySQL) mithilfe der ADO-Datenbankkomponenten herstellt. Ich fragte mich, ob es am besten möglich wäre, die Namen der Felder in der Datenbank zu speichern, um später beim Erstellen von SQL-Abfragen leichter darauf Bezug zu nehmen.SQL-Feldnamen und allgemeine SQL-Verwendung mit Delphi speichern

Zuerst machte ich sie eine einfache Konstante, z. c_UserTable_Username, c_UserTable_Password, entschied sich dann aber das war kein besonders guter Weg, Dinge zu tun, so bin ich jetzt sie in einem ständigen Aufzeichnung zB Speicherung:

type 
    TUserTable = record 
    TableName : String; 
    Username : String; 
    Password : String; 
end; 

const 
UserTable : TUserTable = 
    (
     TableName : 'users'; 
     Username : 'Username'; 
     Password : 'Password'; 
    ); 

dies ermöglicht es mir, eine Aussage darüber zu erstellen, wie:

query.SQL.Add('SELECT ' + UserTable.Username + ' FROM ' + UserTable.TableName); 

und müssen sich keine Sorgen über harte Codierung der Feldnamen usw. machen

Ich habe jetzt in das Problem, aber wo ich, wenn ich durch die Tabellenfelder (zum Beispiel wenn es 20 Felder oder so) sind, kann ich laufen t. Ich muss die Datensatzreferenz für jedes Feld manuell eingeben.

Ich denke, was ich gerne wissen würde ist, ob es eine Möglichkeit gibt, alle Feldnamen auf einmal oder einzeln zu iterieren; oder gehe ich das falsch herum? Vielleicht sollte ich sie nicht so lagern?

Außerdem habe ich eine "Database" -Klasse gemacht, die im Grunde Methoden für viele verschiedene SQL-Anweisungen enthält, zum Beispiel GetAllUsers, GetAllProducts, usw. Klingt das korrekt? Ich habe mir viele Delphi/SQL-Tutorials angeschaut, aber sie scheinen Ihnen nicht lange zu zeigen, wie Sie Abfragen ausführen.

Ich nehme an, ich bin nur ein wenig verloren und jede Hilfe ist sehr willkommen. Danke :)

+1

Suche nach dem Artikel Analysieren von DataSets (in Delphi und Kylix). Verwenden Sie den Code dort, um eine Generatorart der Anwendung zu schreiben. – user114285

Antwort

2

Nun, Sie sind hart Codierung Feldnamen; Sie codieren sie einfach in const und nicht in der Abfrage selbst. Ich bin mir nicht sicher, ob das wirklich etwas verbessert. Soweit geht durch die Felder laufen, versuchen Sie dies:

var 
    Field: TField; 
begin 
    for Field in query.Fields do begin 
    // do stuff with Field 
    end; 
end; 

eher eine „Datenbank“ Klasse als zu machen, würde ich wahrscheinlich eine TDataModule verwenden. Dies funktioniert fast genauso wie Ihre Klasse, mit der Ausnahme, dass Sie Abfragen zur Entwurfszeit interaktiv entwerfen können. Sie können beliebige Methoden auf dem DataModule platzieren.

Dies macht es auch sehr einfach, persistente TFields zu instanziieren (siehe Hilfe zu diesem Thema), was Ihnen vielleicht eher gefällt, als mit dem Speichern von Feldnamen.

+0

Suche nach dem Artikel Analysieren von DataSets (in Delphi und Kylix) – user114285

3

Sie könnten Ihre Abfragen auch als RESOURCESTRING speichern, was es ermöglichen würde, sie nachträglich mit einem Ressourceneditor zu bearbeiten (falls erforderlich).

RESOURCESTRING 
    rsSelectFromUsers = 'SELECT USERNAME FROM USERS '; 

Ihr Ansatz einer Datenbankklasse funktioniert sehr gut. Ich habe genau das in mehreren meiner Projekte getan, indem ich eine Schnittstelle zu einem Objekt zurückgebe, das das Dataset enthält. Der Vorteil davon ist, dass die Datenmenge geschlossen und gelöscht wird, wenn die zurückgegebene Schnittstellenvariable den Gültigkeitsbereich verlässt.

1

Wenn Sie eine Datenbankklasse wie abgebildet verwenden möchten, sollten Sie die Fähigkeit von Datensätzen berücksichtigen, Funktionen in D2007 und höher zu enthalten.

Zum Beispiel Ihr Beispiel würde:

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; 

, die erlaubt:

query.SQL.add(userTable.sqlGetUserName); 

oder

query.SQL.add(userTable.sqlGetUserName(Format('%s=%s', [userTable.userName,'BOB'])); 

ich wirklich nicht empfehlen mit SQL direkt, wie Sie haben illustriert. Meiner Meinung nach sollten Sie niemals direkte SQL-Aufrufe an die Tabellen haben. Das führt zu einer starken Kopplung zwischen der Benutzeroberfläche und der Datenbank (die nicht existieren sollte) und verhindert, dass Sie eine hohe Sicherheitsstufe für die direkte Tabellenmodifikation festlegen.

Ich würde alles in gespeicherte Procs einbinden und eine DB-Interface-Klasse haben, die den gesamten Datenbankcode in ein Datenmodul kapselt. Sie können die direkten Links zu datensensitiven Komponenten weiterhin von einem Datenmodul aus verwenden, Sie müssen lediglich die Links mit dem DM-Namen voranstellen.

Zum Beispiel, wenn Sie eine Klasse wie gebaut:

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; 

Sie haben jetzt die Möglichkeit, eine Form von Unit-Tests auf Ihrer Datenschnittstelle zu tun, haben Sie die SQL von der Benutzeroberfläche entfernt und die Dinge sind hoch schauen. Sie haben noch eine Menge hässliche SQL in Ihrem Code-Schnittstelle, obwohl, so dass über gespeicherte Procs bewegen und Sie erhalten:

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; 

Sie nun einige dieser Funktionen in einem ADO-Thema und die UI würde keine Ahnung haben, bewegen konnte Das Hinzufügen oder Löschen von Benutzern erfolgt in einem separaten Prozess. Beachten Sie, dass dies sehr einfache Operationen sind. Wenn Sie also nützliche Dinge tun möchten, wie das Benachrichtigen des übergeordneten Elements, wenn der Prozess abgeschlossen ist (um beispielsweise nach dem Hinzufügen/Löschen/Aktualisieren eine Benutzerliste zu aktualisieren), müssen Sie dies in das Threading einprogrammieren Modell.

die gespeicherte Prozedur für das Add-Code BTW, wie folgt aussieht:

create procedure [dbo].[usp_addUser](@userName varchar(20), @password varchar(20)) as 
if (select count(userName) from users where [email protected])=0 
    insert into users (userName, password) values (@userName,@password) 
else 
    update users set [email protected], [email protected] where [email protected] 

Auch ein wenig Haftungsausschluss: Dieser Beitrag ziemlich lang ist und, während ich versuchte, die meisten davon zu überprüfen, habe ich etwas verpasst haben Irgendwo.

0

Nehmen Sie eine Beute bei Analysing DataSets (in Delphi and Kylix)

Der Code ein gutes Beispiel für die Manipulation Tabellenmetadaten ist. Sie können Feldnamen erhalten und dann einen Code-Generator schreiben, der eine Basiseinheit/oder den Schnittstellenteil davon erzeugen kann.

1

Vielleicht ein bisschen off Thema, aber Sie könnten Data Abstract von RemObjects verwenden.