Einzelnen Beitrag anzeigen

Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#9

AW: Datenbankzugriff, Threads... Verständnis

  Alt 16. Feb 2012, 13:52
Was ich mir basteln will schaut in etwa wie folgt aus -- ich schreibe das nun rein aus dem Gedächtnis und weiß nicht, ob alles so umsetzbar ist, da ich es noch nicht zu 100% umgesetzt habe.
Delphi-Quellcode:
type
{ hier mal die Schnittstellen, wie ich sie mir grob vorstelle... }
{ keine korrekte Implementierung; in logischer Reihenfolge präsentiert }

IDbConnectionInformation = interface
  function ToString(): string;
end;

IDbConnection = interface
  procedure SetConnectionInformation(AConnectionInformation: IDbConnectionInformation);
  procedure Open();
  procedure Close();
  function GetQuery(): IDbQuery;
end;

IDbConnectionManager = interface
  procedure SetConnectionInformation(AConnectionInformation: IDbConnectionInformation; ADatabaseID: string = 'default');
  function GetConnection(ADatabaseID: string = 'default'): IDbConnection;
end;

IDbQuery = interace
  procedure SetSQL(ASQL: string);
  // procedure SetParam() -> muss ich noch schauen, wie das mache
  procedure Execute();
  function Open(): IDbDatasetEnumerator; // zum einfachen durchgehen der Datensätze
  procedure Close();
end;

IDbDataset = interface
  // für die Beschreibung eher irrelevant
end;

IDbDatasetEnumerator = interface
  // für die Beschreibung eher irrelevant
end;


// Nun eine Basisimplementierung, die die Idee von oben repräsentiert. Der Rest
// der Interfaces ist für die Idee eher uninteressant, daher überlasse ich es
// dem Leser das mal auszuprogrammieren ;)
TBaseDbManager = class(TInterfacedObject, IDbConnectionManager)
private
  FConnectionInformation : TDictionary<string, IDbConnectionInformation>;
  FConnections : TDictionary<TThreadID, IDbConnection>;
public
  function GetConnection(ADatabaseID: string = 'default'): IDbConnection;
end;

function TBaseDbManager.GetConnection(ADatabaseID: string = 'default'): IDbConnection;
begin
  FCriticalSection.Enter();
  try
    // Wenn noch keine Verbindung für den aktuellen Thread besteht, dann wird eine
    // aufgebaut
    if (not FConnections.ContainsKey(GetCurrentThreadID())) then
      FConnections.Add(GetCurrentThreadID(), DbConnectionFactory.Create(FConnectionInformation[ADatabaseID]));

    // Anschließend existiert immer eine Verbindung für den aktuellen
    // Thread, also kann eine zurückgegeben werden
    Result := FConnections[ADatabaseID];
  finally
    FCriticalSection.Leave();
  end;
end;
  


{ Nun zur Implementierung, aber nur die wichtigsten Stellen... }

// Beim Start wird der DbConnectionManager mit den nötigen Informationen gefüttert, sodass
// dieser alle Daten für den Verbindungsaufbau hat und somit automatisch Verbindungen
// zurückgeben kann.
// Ich habe einen globalen ServiceLocator, der mir auf Anfrage eine Singleton-Instanz
// eines speziellen Typs, der sich vorher registriert hat zurück gibt. Natürlich kann man
// sowas auch anders lösen.
procedure TApplication.Startup();
var
  DbConnectionInformation : TDictionary<string, IDbConnectionInformation>;
  DbConnectionManager : IDbConnectionManager;
begin
  DbConnectionInformation := LoadAndBuildDbConnectionInformation();
  // z.B.
  // DbConnectionInformation['default'] = Verbindung zu Oracle
  // DbConnectionInformation['access'] = Verbindung zu Access
  // DbConnectionInformation['access-lan'] = Verbindung zu Access-DB, die im LAN liegt

  // Fülle den DbManager mit den Verbindungsinformationen
  DbConnectionManager := ServiceLocator.GetService('database');
  for ConnectionInformation in DbConnectionInformation do
    DbConnectionManager.AddConnectionInformation(ConnectionInformation);
end;



{ und hier nun die Nutzung... für jeden Thread wird hier automatisch }
{ eine Verbindung aufgebaut und darüber gearbeitet. }

var
  DbConnectionManager : IDbConnectionManager;
  DbConnection : IDbConnection;
  Query : IDbQuery;
  Dataset : IDbDataset;
begin
  DbConnectionManager := ServiceLocator.GetService('database')
  DbConnection := DbConnectionManager.GetConnection();
  Query := DbConnection.GetQuery();

  // Natürlich geht auch
  Query := ServiceLocator.GetService('database').GetConnection().GetQuery();

  // Oder wenn man auf eine andere DB zugreifen will. Vorher muss man aber die
  // entsprechende ConnectionInfo dem DbConnectionManager zugeführt haben.
  Query := ServiceLocator.GetService('database').GetConnection('sqlite').GetQuery();

  Query.SetSQL('SELECT * FROM BlubTable');
  try
    for Dataset in Query.Open() do
    begin
      ShowMessage(Dataset.GetField('Text').ToString());
    end;
  finally
    Query.Close();
  end;
end;
Schön an der Geschichte finde ich auch, dass durch die Interface-Nutzung man sehr viel Spielraum hat. Ebenso hängt es nun nicht mehr von der darunter liegenden DB ab. Für die konkrete Instanziierung eines Objekts nutze ich Factories, die dann eine entsprechende Instanz erzeugen und passend verknüpfen.
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat