AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Delphi Datenbankzugriff, Threads... Verständnis
Thema durchsuchen
Ansicht
Themen-Optionen

Datenbankzugriff, Threads... Verständnis

Ein Thema von haentschman · begonnen am 15. Feb 2012 · letzter Beitrag vom 17. Feb 2012
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#1

Datenbankzugriff, Threads... Verständnis

  Alt 15. Feb 2012, 19:23
Datenbank: Firebird • Version: 2.5 • Zugriff über: IBDAC
Hallo alle...

kann mir jemand bitte die Zusammenhänge erklären wie man den Datenbankzugriff, Transaktionen und mehrere Threads miteinander verknüpfelt ?
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

geht das überhaupt ? Wenn ja was muß wo hin ?

Scenario 3:
- eine SP die weiterführenden Änderungen übernimmt (Erklärung siehe Hintergrund)
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
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#2

AW: Datenbankzugriff, Threads... Verständnis

  Alt 15. Feb 2012, 19:38
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.
In wie weit das immer sicher funktioniert und ob es mit einer Transaktion threadübergreifend arbeitet ... k.A.
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#3

AW: Datenbankzugriff, Threads... Verständnis

  Alt 15. Feb 2012, 19:56
...gerade gefunden...
Zitat:
Zitat von http://www.devart.com/ibdac/faq.html:
Yes, IBDAC is thread-safe but there is a restriction. The same TIBCConnection object cannot be used in several threads. So if you have a multithreaded application, you should have a TIBCConnection object for each thread that uses IBDAC.
... damit fällt Szenario 2 raus... Gott sei Dank... An die Umbauarbeit wollte ich nicht denken.

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 ?
  Mit Zitat antworten Zitat
tsteinmaurer

Registriert seit: 8. Sep 2008
Ort: Linz, Österreich
530 Beiträge
 
#4

AW: Datenbankzugriff, Threads... Verständnis

  Alt 15. Feb 2012, 20:38
ad Szenario 3: Ein SP-Aufruf ist synchron, d.h. der aufrufende Thread ist so lange blockiert, bis die SP abgearbeitet wurde.
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#5

AW: Datenbankzugriff, Threads... Verständnis

  Alt 15. Feb 2012, 21:20
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...
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#6

AW: Datenbankzugriff, Threads... Verständnis

  Alt 16. Feb 2012, 11:00
Moin... gleiches Thema nächste Zwickmühle.

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...
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

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

AW: Datenbankzugriff, Threads... Verständnis

  Alt 16. Feb 2012, 11:14
Dann gebe ich dem Thread die DB Info´s mit und er macht eine extra Connection auf... (Szenario 1).
Gestalte das ganze doch noch etwas abstrakter: Warum bittest du nicht einen "DB-Manager" um eine Query und arbeitest dann mit dieser?! Beim Start der Anwendung musst du dem DB-Manager lediglich einmal die Connection-Informationen übergeben und gut ist das. Hast du Verbindungen zu mehreren DBs, so kannst du auch sowas machen DbManager.GetQuery('oracle'); und 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...

welche Lösung wäre akzeptabel ?
Du weißt doch von vorn herein, was genau du updaten musst, oder? Dann packe das ganze doch in einen einzigen Thread und Transaktion und gut ist. Wie lange können solche Updates denn eigentlich dauern?! oO
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)

Geändert von s.h.a.r.k (16. Feb 2012 um 11:16 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#8

AW: Datenbankzugriff, Threads... Verständnis

  Alt 16. Feb 2012, 12:04
Danke...
Zitat:
gestalte das ganze doch noch etwas abstrakter: Warum bittest du nicht einen "DB-Manager" um eine Query und arbeitest dann mit dieser?! Beim Start der Anwendung musst du dem DB-Manager lediglich einmal die Connection-Informationen übergeben und gut ist das.
Zitat:
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.
...ich weis zwar wie du das meinst, kann das aber praktisch nicht umsetzen. Könntest du das etwas genauer erklären ? Vieleicht mit einem Schema (vereinfacht) des Quelltextes ? So nach dem Motto wer ruft von wo was auf, wer erzeugt was ?

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:
Dann packe das ganze doch in einen einzigen Thread und Transaktion und gut ist.
PS: ich habe die Geschwindigkeit nicht gemessen. Normalerweise geht das schnell. Über den Tellerrand blickend wäre die Konstellation, Laden erfolgt vor dem Commit, durchaus möglich.

Geändert von haentschman (16. Feb 2012 um 12:15 Uhr)
  Mit Zitat antworten Zitat
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
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#10

AW: Datenbankzugriff, Threads... Verständnis

  Alt 17. Feb 2012, 08:23
Danke an alle...

Die Lösung ist viel einfacher... Ihr könnt mich prügeln. 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

Sorry für die Belästigung...
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:49 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz