![]() |
Datenbank: Firebird • Version: 2.5 • Zugriff über: IBDAC
Datenbankzugriff, Threads... Verständnis
Hallo alle... 8-)
kann mir jemand bitte die Zusammenhänge erklären wie man den Datenbankzugriff, Transaktionen und mehrere Threads miteinander verknüpfelt ? :wink: Szenario 1: - Thread hat seine eigene Connection - Thread hat seine Query´s - Thread hat seine eigene Transaktionen (wenn benötigt) ...sollte ohne Probleme funktionieren. Szenario 2: - eine Connection - gemeinsame Transaktion - alle Threads sollen die gemeinsame Connection, die aktuelle Transaktion benutzen :gruebel: geht das überhaupt ? Wenn ja was muß wo hin ? Scenario 3: - eine SP die weiterführenden Änderungen übernimmt (Erklärung siehe Hintergrund) :gruebel: Wartet die Hauptanwendung auf das Beenden der SP oder geht´s sofort weiter nach Abschicken des Aufrufes. Hintergrund: Ich habe ein kleines DB Framework gebastelt. Normalerweise sind wenig Daten wegzuschreiben. Das merkt man kaum (auch ohne Threads). In sehr wenig Fällen sind bei Änderung eines Datensatzes in Tabelle1 auch Änderungen in Tabelle2 notwendig. Die Anzahl der zu änderten DS sind ansteigend hoch. Das wollte ich gern auslagern. Danke für Info´s |
AW: Datenbankzugriff, Threads... Verständnis
Das Erste sollte wohl ohne Probleme gehen.
Ansonsten bieten einige Datenbankkomponenten sowas wie ThreadPooling, welches man aktivieren müßte, um dort vermutlich mit mehreren Threads auf einer Connection zu arbeiten. :gruebel: In wie weit das immer sicher funktioniert und ob es mit einer Transaktion threadübergreifend arbeitet ... k.A. |
AW: Datenbankzugriff, Threads... Verständnis
...gerade gefunden...
Zitat:
dann bleibt nur Szenario 3 und die Frage der Ausführung. Wenn die SP über einen Trigger des "Masterdatensatzes" angeworfen wird sollte doch nach dem Ändern des einen Datensatzes die Anwendung weitermachen. Oder wartet die auch auf die SP ? |
AW: Datenbankzugriff, Threads... Verständnis
ad Szenario 3: Ein SP-Aufruf ist synchron, d.h. der aufrufende Thread ist so lange blockiert, bis die SP abgearbeitet wurde.
|
AW: Datenbankzugriff, Threads... Verständnis
Danke an alle...
Gut. Dann gebe ich dem Thread die DB Info´s mit und er macht eine extra Connection auf... (Szenario 1). Ist immer wieder schön mit Euch zu diskutieren...8-) |
AW: Datenbankzugriff, Threads... Verständnis
Moin... gleiches Thema nächste Zwickmühle. :roll:
Hintergrund: - Tabelle 1, Feld "Bezeichnungen" wird geändert - Tabelle 2, Feld "Bezeichnung" muß angepaßt werden (kann lange dauern) - Tabelle 3, Feld "Bezeichnung" muß angepaßt werden (kann lange dauern) Ablauf: (stark vereinfacht) 1. SaveObjects 1.1 Update Tabelle 1 -> Execute 1.2 Thread für Update Tabelle 2 und 3 (eigene Connection/Transaktion) 2. LoadLists -> Problem: Datenkonsistenz ist nicht gewährleistet. Wenn in Tabelle 2 oder 3 was schiefläuft ist Tabelle 1 geschrieben da nicht in der selben Transaktion -> Lösung ?: notfalls im Thread den Datensatz in Tabelle 1 wieder auf den ursprünglichen Wert setzen... mögliche Lösung: - alle Updates in den Thread -> Problem: unter Umständen sind die Daten aller 3 Tabellen noch nicht Committed und das LoadLists holt noch die alten Daten welche Lösung wäre akzeptabel ? Danke... |
AW: Datenbankzugriff, Threads... Verständnis
Zitat:
Delphi-Quellcode:
und
DbManager.GetQuery('oracle');
Delphi-Quellcode:
.
DbManager.GetQuery('access');
In dieser GetQuery()-Methode wird dann automatisch geprüft, welcher Thread aufgerufen wird. Ist für den Thread noch keine Verbindung aktiv, so wird eine neue aufgebaut und die Query in diesem Kontext erzeugt. Zudem würde ich das ganze dann auch noch mit Interfaces verknüpfen, was dazu führt, dass du dich weniger um die Speicherverwaltung kümmern musst. Ebenso abstrahierst du dann auch von der genutzten Datenbank und bietest deinem Programm eine einheitliche Schnittstelle für den DB-Zugriff an. Leider ist das ganze bisher nur in meinem Kopf und noch nicht ganz umgesetzt ;) Bin aber auf dem besten Weg gerade... Zitat:
|
AW: Datenbankzugriff, Threads... Verständnis
Danke...
Zitat:
Zitat:
Derzeitiger Stand: 1. DB Framework (eine Klasse, vorbereitet um mal ein Interface draus zu machen (Getter/Setter) ) 1.1 von außen kann ich ggf. eine Transaktion öffnen wenn benötigt. Intern haben die zusammengehörigen SQL eine Transaktion für sich - Save... procedure Save(aPlantScheme: TeAV_PlantScheme) - anhand der Property PlantScheme.State wird unterschieden ob Insert/Update/Delete in update Prozedure: - Query1. Execute (Transaktion der Connection) - Thread erzeugen - im Thread Query2 und Query 3 (seperate Connection, eigene Transaktion) wenn ich mir vom DB Framework eine Query hole (vom Thread aus ?) kann der ganze Kram trotzdem erst gemeinsam Commited werden. (evt. zu lange) Zitat:
|
AW: Datenbankzugriff, Threads... Verständnis
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:
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.
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; |
AW: Datenbankzugriff, Threads... Verständnis
Danke an alle...
Die Lösung ist viel einfacher... Ihr könnt mich prügeln. 8-) Normalerweise kommt immer als erstes das Stichwort Normalisierung. Ich habe diese alte DB Struktur "normalisiert" und diese Felder einfach übersehen. In Tabelle 2 und 3 einfach die ID aus Tabelle 1 ablegen...und gut :duck: Sorry für die Belästigung... :lol: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:14 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz