[Dresden-pm] Re: Perl: gemeinsames dbh-Handle über mehrere Objekte

Steffen Schwigon schwigon at webit.de
Mon Jul 21 12:49:53 CDT 2003


[ CC: Perl-Mongers-Mailingliste, für's Archiv :-) ]

"Jens Puruckherr" <jpuruckherr at 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
-- 
Steffen Schwigon <schwigon at webit.de>
Dresden Perl Mongers <http://dresden-pm.org/>