Hallo,
ich habe eine ähnliche Frage hier schon mal gestellt, allerdings haben die Vorraussetzungen sich etwas geändert. Darum noch mal von vorne:
Eine Perl-Applikation besteht aus verschiedenen Klassen (Shop, Artikel, Hersteller,Kunde ....) z.B. ein Artikel besteht u.a. aus mehrere Bild-Objekten, einem Hersteller-Objekt, Attribut-Objekten .... Beim Schreiben des Artikels in die DB soll nun eine mysql-Transaktion zum Einsatz kommen.
Alle Klassen erben von der Shop-Klasse die Fähigkeit ein connect auf die DB zu machen. Allerdings wird dabei jedesmal ein neues DB-Handle erzeugt, womit meine Transaktion in die Hosen geht.
Als einzigste Lösung fällt mir ein, in der Applikation selber ein initiales Shop-Objekt zu erzeugen, dass eine DB-Verbindung aufbaut. Dieses Handle wird allen anderen Objekten bei der Initialisierung übergeben. Initialisiert ein Objekt ein weiteres, übergibt es dieses Händle einfach weiter. Aber irgendwie gefällt mir das nicht so recht ... Irgendwann hat die DB ein Timeout und das Handle ist ungültig ... Mir schwebt eher eine Lösung vor, bei der das aktuell gültige DB-Handle via Methodenaufruf abgeholt werden kann und alle Objekte bekommen so automatsich das gleiche Handle. Das erfolgt sinnigerweise unmittelbar vor Start der DB-Transaktion. Aber wie bewerkstellige ich das?.
Oder gibt es für sowas eine ganz andere Herangehensweise?
Jens
[ CC: Perl-Mongers-Mailingliste, für's Archiv :-) ]
"Jens Puruckherr" jpuruckherr@cyberport.de writes:
Eine Perl-Applikation besteht aus verschiedenen Klassen (Shop, Artikel, Hersteller,Kunde ....) z.B. ein Artikel besteht u.a. aus mehrere Bild-Objekten, einem Hersteller-Objekt, Attribut-Objekten .... Beim Schreiben des Artikels in die DB soll nun eine mysql-Transaktion zum Einsatz kommen.
Alle Klassen erben von der Shop-Klasse die Fähigkeit ein connect auf die DB zu machen. Allerdings wird dabei jedesmal ein neues DB-Handle erzeugt, womit meine Transaktion in die Hosen geht.
Als einzigste Lösung fällt mir ein, in der Applikation selber ein initiales Shop-Objekt zu erzeugen, dass eine DB-Verbindung aufbaut. Dieses Handle wird allen anderen Objekten bei der Initialisierung übergeben. Initialisiert ein Objekt ein weiteres, übergibt es dieses Händle einfach weiter. Aber irgendwie gefällt mir das nicht so recht ... Irgendwann hat die DB ein Timeout und das Handle ist ungültig ... Mir schwebt eher eine Lösung vor, bei der das aktuell gültige DB-Handle via Methodenaufruf abgeholt werden kann und alle Objekte bekommen so automatsich das gleiche Handle. Das erfolgt sinnigerweise unmittelbar vor Start der DB-Transaktion. Aber wie bewerkstellige ich das?.
Oder gibt es für sowas eine ganz andere Herangehensweise?
Ich strukturiere es tatsächlich immer anders. Im folgenden mein persönlicher Weg, man kann es auch so machen, wie Du es oben beschrieben hast. Ist mir aber zu wenig abstrakt, IMHO sollten die Applikationsklassen Shop, Artikel, Hersteller usw. kaum was von der Datenbanksache mitbekommen.
*****
Ich gehe mal davon aus, daß sich MySQL-Transaktionen wie herkömmliche Datenbanken verhalten, ich kenne nur Oracle und PostgreSQL. Und ich rede vom Datenbankzugriff via DBI.
Ich habe meist eine zentrale Klasse "DbHandling". Diese kann als einzige connecten.
Darin bringe ich Methoden zum Zugriff auf die DB unter, entweder Klassenspezifisch, z.B.
my $shop = Shop->new(); my $hersteller = Hersteller->new(); $dbHandling->saveShop ($shop); $dbHandling->saveHersteller ($hersteller);
oder komplett generisch
$dbHandling->save ($shop); $dbHandling->save ($hersteller);
o.ä.
Im generischen Fall erben Shop, Hersteller, Artikel usw. z.B. von einer gemeinsamen Klasse "Table", die mit dem DbHandling kooperiert. DbHandling arbeitet dann immer nur mit Objekten vom Typ "Table", also deren Methoden-API.
Jedes Table-Objekt implementiert dann ggf. spezifische Methoden der Table-API und bringt alle notwendigen Infos mit, die DbHandling braucht.
In der Applikation erzeuge ich dann eine Transaktionsklammer durch
$dbHandling->connect(); # $dbHandling->do_something_useful(); $dbHandling->commit();
Damit sparst Du Dir zumindest mal das Durchreichen von Handles durch alle Klassen und hältst Shop, Hersteller, usw. von DB-Logik frei.
Manchmal lagere ich auch Funktionalität aus, z.B. in ein
ShopHandling HerstellerHandling usw.
damit Shop und Hersteller komplett nur Datencontainer sind. Geschmackssache und fallabhängig.
Timeouts auf das DB-Handle sollte die Datenbank nicht haben, falls doch, ist es ein generelles Problem, daß Deine Transaktion länger dauert, als es die DB erlaubt. Dann dreht man einfach die Parameter an der DB entsprechend hoch oder sturkturiert die Applikation anders.
Mehrere voneinander unabhängige Transaktionen kannst Du durch verschiedene DbHandling-Objekte erreichen.
Mein DbHandling hat in sich auch nochmal eine Klassenstruktur, um allgemeine Dinge und applikationsspezifische Methode zu trennen.
Für das Schreiben von generisch zusammenspielenden "Table"- und "DbHandling"-Klassen kannst Du Dir auf CPAN mal die "DBIx::DBSchema"- Klassen angucken. Mit denen kannst Du wohl Deine Shop- und Hersteller-Beziehungen in abstraktere, applikationsgerechtere OO-Strukturen unterbringen, habe ich aber selber auch noch nicht genutzt.
******
Falls Du jetzt noch von Web-Applikationen, z.B. mit mod_perl redest, besteht das Problem, daß Du ein DB-Handle nicht garantiert über zwei Requests gerettet bekommst, ohne einen Applikationsserver einzusetzen, oder den Apache absurd zu konfigurieren.
Das bedeutet praktisch, Du mußt Dir Deine Transaktionsdinge über mehrere Seiten manuell zusammenorganisieren und machst am Ende innerhalb eines einzigen Web-Requests alles zusammen: connect(), logik() und commit(). Also immer nur recht kurze Transaktionen.
Letzteres ist ein Hobbyproblem von mir, da können wir gerne mal bei einem Malzbier bei 'nem Perl-Mongers-Treffen in Ruhe drüber reden. :-)
GreetinX Steffen
lug-dd@schlittermann.de writes:
Ich strukturiere es tatsächlich immer anders. Im folgenden mein persönlicher Weg, man kann es auch so machen, wie Du es oben beschrieben hast. Ist mir aber zu wenig abstrakt, IMHO sollten die Applikationsklassen Shop, Artikel, Hersteller usw. kaum was von der Datenbanksache mitbekommen.
....
Darin bringe ich Methoden zum Zugriff auf die DB unter, entweder Klassenspezifisch, z.B.
my $shop = Shop->new(); my $hersteller = Hersteller->new(); $dbHandling->saveShop ($shop); $dbHandling->saveHersteller ($hersteller);
Das scheint mir recht handlich und schnell umsetzbar. Danke. Generell die DB-Geschichte aus den Klassen rauszunehmen und in eine Wrapper-Klasse ainzubauen, werde ich mir für die Zkunft vornehmen. Jetzt muss ich aber erst mal paar Ergebnisse vorzeigen.... Dann wird das ganze noch via Webservice angesteuert ... lecker ;-) Wir sprechen uns dann bestimmt nochmal .....
Achso ... und als cgi läuft es garantiert nicht.
Danke für den Denkanstoss
Mit freundlichen Grüßen
Jens Puruckherr
lug-dd@mailman.schlittermann.de