DBIx :: Class ist eine beliebte Perl-Schnittstelle zu jeder Datenbank, zu der Sie über DBI eine Verbindung herstellen können . Es gibt eine gute Dokumentation für die technischen Details, aber nur wenige Informationen über die ordnungsgemäße Verwendung (einschließlich der Situationen, in denen Sie dies wahrscheinlich nicht möchten).
In vielen Situationen greifen die Leute reflexartig danach, weil sie denken, sie sollten es für alles verwenden, was eine Datenbank betrifft. Am häufigsten habe ich jedoch gesehen, dass es so oft missbraucht wurde, bis es zu einem Schmerzpunkt wurde. Meine Frage während der Code- und Architekturüberprüfungen lautet immer: "Welchen Nutzen bietet Ihnen Foo?" Meistens können die Entwickler, die ich in diesen Situationen sehe, keine kohärente Antwort darauf finden. Andererseits verstehen sie auch oft einfaches SQL nicht.
In den letzten Monaten habe ich die Leute gefragt, warum Sie DBIx :: Class verwenden. und nur eine gute Antwort erhalten haben (und diese Person war auch in der Lage, das Follow-up "Wann würden Sie es nicht verwenden" zu beantworten). Peter Rabbitson, der Hauptentwickler, kam in seinem Interview auf FLOSS Weekly einer Antwort sehr nahe , aber es ist etwas vergraben in der Mitte des Interviews.
Wie kann ich entscheiden, ob die Verwendung von DBIx :: Class für ein Projekt geeignet ist?
Antworten:
Bevor ich die Frage beantworte, denke ich, dass einige Hintergründe angebracht sind.
Der Kern des Problems
Nachdem ich jahrelang Entwickler interviewt und eingestellt habe, habe ich zwei Dinge gelernt:
Die überwiegende Mehrheit der Entwickler verfügt über sehr wenig Erfahrung im Datenbankdesign.
Ich habe eine lockere Korrelation zwischen denen, die Datenbanken nicht verstehen, und denen, die ORMs hassen, festgestellt.
(Anmerkung: und ja, ich weiß, dass es diejenigen gibt, die Datenbanken sehr gut verstehen und ORMs hassen.)
Wenn die Leute nicht verstehen , warum Fremdschlüssel sind wichtig, warum Sie nicht die Herstellernamen in der einbetten
item
Tabelle, oder warumcustomer.address1
,customer.address2
undcustomer.address3
Felder sind keine gute Idee, eine ORM Hinzufügen , um es für sie leichter zu schreiben Datenbank Bugs wird nichts helfen.Stattdessen sind ORMs mit einer ordnungsgemäß gestalteten Datenbank und einem OLTP-Anwendungsfall Gold wert. Die meisten der Grunzenarbeit weggeht und mit Werkzeugen wie DBIx :: Class :: Schema :: Loader kann ich von einem guten Datenbankschema gehen zu Perl - Code in Minuten arbeiten. Ich würde die Pareto-Regel zitieren und sagen, dass 80% meiner Probleme mit 20% der Arbeit gelöst wurden, aber in Wirklichkeit finde ich die Vorteile noch größer.
Missbrauch der Lösung
Ein weiterer Grund, warum manche Leute ORMs hassen, ist, dass sie die Abstraktion auslaufen lassen. Betrachten wir den allgemeinen Fall von MVC-Webanwendungen. Folgendes sehen wir häufig (Pseudocode):
Die Leute schreiben so Controller-Routen und klopfen sich auf den Rücken, weil sie denken, es sei guter, sauberer Code. Sie wären entsetzt darüber, SQL in ihren Controllern hart zu codieren, aber sie haben kaum mehr getan, als eine andere SQL-Syntax aufzudecken. Ihr ORM-Code muss in ein Modell übertragen werden, und dann können sie dies tun:
Weißt du was jetzt passiert ist? Sie haben Ihr Modell ordnungsgemäß gekapselt, den ORM nicht verfügbar gemacht und später, wenn Sie feststellen, dass Sie diese Daten aus einem Cache anstelle der Datenbank abrufen können, müssen Sie Ihren Controller-Code nicht ändern (und das ist einfacher Tests schreiben und die Logik wiederverwenden).
In Wirklichkeit verlieren die Benutzer ihren ORM-Code auf allen Controllern (und Ansichten), und wenn sie auf Skalierbarkeitsprobleme stoßen, geben sie eher dem ORM als ihrer Architektur die Schuld. Das ORM bekommt einen schlechten Ruf (das sehe ich bei vielen Kunden immer wieder). Verstecken Sie stattdessen diese Abstraktion, damit Sie, wenn Sie die ORM-Grenzen tatsächlich erreicht haben, geeignete Lösungen für Ihr Problem auswählen können, anstatt den Code so eng mit dem ORM zu verknüpfen, dass Sie sich nicht mehr auskennen.
Berichterstattung und andere Einschränkungen
Wie Rob Kinyon oben klargestellt hat, ist die Berichterstattung in der Regel eine Schwäche in Bezug auf ORMs. Dies ist eine Teilmenge eines größeren Problems, bei dem kompliziertes SQL oder SQL, das sich über mehrere Tabellen erstreckt, mit ORMs manchmal nicht gut funktioniert. Beispielsweise erzwingt der ORM manchmal einen Join-Typ, den ich nicht möchte, und ich kann nicht sagen, wie das behoben werden soll. Oder vielleicht möchte ich einen Index-Hinweis in MySQL verwenden, aber es ist nicht einfach . Oder manchmal ist die SQL einfach so verdammt kompliziert, dass es besser wäre, die SQL zu schreiben, als die bereitgestellte Abstraktion.
Dies ist einer der Gründe, warum ich angefangen habe, DBIx :: Class :: Report zu schreiben . Bisher funktioniert es gut und löst die meisten Probleme, die die Leute hier haben (sofern sie mit einer schreibgeschützten Oberfläche einverstanden sind). Und während es in der Realität wie eine Krücke aussieht , macht es die Arbeit mit
DBIx::Class
noch einfacher , solange Sie Ihre Abstraktion nicht verlieren (wie im vorherigen Abschnitt erläutert) .Wann würde ich DBIx :: Class wählen?
Für mich würde ich es meistens wählen, dass ich eine Schnittstelle zu einer Datenbank benötige. Ich benutze es seit Jahren. Ich würde es jedoch möglicherweise nicht für ein OLAP-System auswählen, und neuere Programmierer werden mit Sicherheit damit zu kämpfen haben. Außerdem stelle ich häufig fest, dass ich Metaprogramme benötige, und obwohl
DBIx::Class
die Tools zur Verfügung gestellt werden, sind sie sehr schlecht dokumentiert.Der Schlüssel zur
DBIx::Class
korrekten Verwendung ist der gleiche wie bei den meisten ORMs:Leck die Abstraktion nicht aus.
Schreiben Sie Ihre verdammten Tests.
Wissen, wie Sie bei Bedarf auf SQL zugreifen können.
Erfahren Sie, wie Sie eine Datenbank normalisieren.
DBIx::Class
Sobald Sie es gelernt haben, erledigen Sie die meisten Aufgaben für Sie und das schnelle Schreiben von Bewerbungen ist ein Kinderspiel.quelle
#dbix-class
und verbracht haben#catalyst
) - der Schlüssel zum "Don't Leak the Abstraction" -Bit ist, dass jede einzelne Sache, mit der Sie in DBIC arbeiten, ist Eine Unterklasse von etwas, das das Verhalten der Ausstechform bereitstellt. Es wird dringend empfohlen, Methoden zu Ihren Unterklassen hinzuzufügen. Sofern Sie keine Q & D-Aufgaben ausführen, sollten nur die von Ihnen geschriebenen Methoden Teil Ihrer öffentlichen Schnittstelle sein.Um zu wissen, wann etwas zu verwenden ist, ist es wichtig zu verstehen, was der Zweck des Dings ist. Was ist das Ziel des Produkts.
DBIx :: Class ist ein ORM - Object-Relational Mapper. Ein ORM nimmt die relationalen / satzbasierten relationalen Datenbankdatenstrukturen und ordnet sie einem Objektbaum zu. Bei der herkömmlichen Zuordnung handelt es sich um ein Objekt pro Zeile, wobei die Tabellenstruktur als Klassenbeschreibung verwendet wird. Eltern-Kind-Beziehungen in der Datenbank werden als übergreifende Beziehungen zwischen Objekten behandelt.
Das sind die Knochen davon. Dies hilft Ihnen jedoch nicht bei der Entscheidung, ob Sie ein ORM verwenden sollten. ORMs sind hauptsächlich dann nützlich, wenn Folgendes zutrifft:
Die größte Stärke eines ORM besteht darin, gutes SQL so zu konstruieren, dass ein Baumdiagramm über die relationale Datenstruktur gelegt wird. Die SQL ist oft haarig und komplex, aber das ist der Preis für die Verwaltung der Impedanz-Fehlanpassung.
Während ORMs sehr gut darin sind, Zeilenextraktions-SQL zu schreiben, sind sie sehr schlecht darin, Sortier-SQL zu schreiben. Dies ist der SQL-Typ, auf dem Berichte basieren. Diese Art der Sortierung wird mit verschiedenen Tools erstellt, nicht mit einem ORM.
Es gibt viele ORMs in verschiedenen Sprachen, einige in Perl. Andere Mapper sind Class :: DBI und Rose :: DB. DBIx :: Class wird häufig aufgrund der Stärke seiner Ergebnismengen als besser angesehen als die anderen. Dies ist ein Konzept, bei dem die SQL-Generierung von der SQL-Ausführung getrennt ist.
Update : Als Antwort auf Ovid bietet DBIx :: Class (über SQL :: Abstract) die Möglichkeit, sowohl die zurückzugebenden Spalten als auch die zu verwendenden Indexhinweise anzugeben.
Im Allgemeinen ist es jedoch besser, wenn Sie dies tun möchten, kein ORM für diese bestimmte Abfrage zu verwenden. Denken Sie daran, dass der Hauptzweck eines ORM darin besteht, Zeilen in einer Tabelle Objekten einer Klasse zuzuordnen, deren Attribute die Spalten der Tabelle sind. Wenn Sie nur einige der Attribute ausfüllen, wissen potenzielle Benutzer dieses Objekts nicht, welche Attribute ausgefüllt sind oder nicht. Dies führt zu einer schrecklichen defensiven Programmierung und / oder einem allgemeinen Hass gegen ORMs.
Fast immer ist der Wunsch, Indexhinweise zu verwenden oder zu begrenzen, welche Spalten zurückgegeben werden, entweder eine Optimierung der Geschwindigkeit und / oder eine aggregierende Abfrage.
Ja, ein guter DBA kann Tabellen mit mehr als 100 MM Zeilen in Oracle oder SQL * Server ausführen. Wenn Sie dies lesen, haben Sie keinen hervorragenden DBA-Mitarbeiter.
Trotzdem kann ein gutes ORM nicht nur Objektdiagramme erstellen, sondern auch eine nachvollziehbare Definition Ihrer Datenbank liefern. Auf diese Weise können Sie Ad-hoc-Abfragen erstellen und wie bei DBI verarbeiten, ohne das Objektdiagramm zu erstellen.
quelle
Als einer der Hauptentwickler der Interchange6-E-Commerce-Plattform (und der primären Schema-Kettensäge) verfüge ich über umfassende Erfahrung mit DBIC. Hier sind einige der Dinge, die es zu einer so großartigen Plattform machen:
Mit dem Abfragegenerator können Sie für viele Datenbankmodule (und mehrere Versionen von jedem) einmal schreiben. Wir unterstützen derzeit Interchange6 mit MySQL, PostgreSQL und SQLite und werden Unterstützung für weitere Engines hinzufügen, sobald wir Zeit und Ressourcen haben. Derzeit gibt es im gesamten Projekt nur zwei Codepfade, die zusätzlichen Code enthalten, um die Unterschiede zwischen den Engines auszugleichen. Dies liegt entweder nur am Fehlen einer bestimmten Datenbankfunktion (SQLite fehlt an einigen Stellen) oder an der Idiotie von MySQL, die sich geändert hat Die Art und Weise, wie die LEAST-Funktion NULL zwischen zwei Nebenversionen verarbeitet.
Vordefinierte Abfragen bedeuten, dass ich einfache Methoden erstellen kann, die (mit oder ohne Argumente) aus dem Anwendungscode aufgerufen werden können, sodass meine Abfragen zumeist in der Schemadefinition verbleiben, anstatt meinen Anwendungscode zu verschwenden.
Durch die Generierung komponierbarer Abfragen können Abfragen in kleine, verständliche vordefinierte Abfragen aufgeteilt und dann zu komplexen Abfragen verkettet werden, die in beiden DBICs nur schwer langfristig verwaltet werden können (noch schlimmer in reinem SQL), wenn sie in einem einzigen Schritt erstellt werden.
Schema :: Loader hat es uns ermöglicht, DBIC mit Legacy-Anwendungen zu verwenden, um ein neues Leben und einen viel einfacheren Weg in die Zukunft zu ermöglichen.
DBIC-Plugins, DeploymentHandler und Migration ergänzen die Tools, die mein Leben vereinfachen, ungemein.
Einer der großen Unterschiede zwischen DBIC und den meisten anderen ORM / ORM-ähnlichen Plattformen besteht darin, dass es Sie zwar bei der Arbeitsweise anleitet, Sie jedoch nicht davon abhält, verrückte Dinge zu tun, die Sie mögen:
Sie können SQL-Funktionen und gespeicherte Prozeduren verwenden, die DBIC nicht kennt, indem Sie einfach den Funktionsnamen als Schlüssel in der Abfrage angeben (dies kann auch zu etwas Spaß führen, wenn Sie versuchen, LEAST in MySQL zu verwenden ^^, aber das ist nicht die Schuld von DBIC). .
Literales SQL kann verwendet werden, wenn es keine 'DBIC-Methode' gibt und das zurückgegebene Ergebnis immer noch in netten Klassen mit Accessoren verpackt ist.
TL; DR Ich würde mich wahrscheinlich nicht darum kümmern, es für wirklich einfache Anwendungen mit nur ein paar Tabellen zu verwenden, aber wenn ich etwas Komplexeres verwalten muss, insbesondere wenn die Kompatibilität mit mehreren Engines und die langfristige Wartbarkeit von entscheidender Bedeutung sind, ist DBIC das Richtige Im Allgemeinen mein bevorzugter Weg.
quelle
(Bevor ich anfange, sollte ich sagen, dass dies nur DBIC-, DBI- und Mojo-basierte DBI-Wrapper vergleicht. Ich habe keine Erfahrung mit anderen Perl-ORMs und werde sie daher nicht direkt kommentieren.)
DBIC macht viele Dinge sehr gut. Ich bin kein schwerer Benutzer davon, aber ich kenne die Theorie. Es leistet eine sehr gute Arbeit bei der SQL-Generierung und insbesondere (wie mir bereits gesagt wurde) beim Umgang mit Joins usw. Es kann auch sehr gut das Prefetching anderer verwandter Daten durchführen.
Aus meiner Sicht ist der größte Vorteil, dass Sie die Ergebnisse DIREKT als Modellklasse verwenden können. Dies wird auch als "Hinzufügen von Ergebnismengenmethoden" bezeichnet, bei denen Sie Ihre Ergebnisse abrufen und Methoden für diese Ergebnisse aufrufen können. Im allgemeinen Beispiel wird ein Benutzerobjekt aus dem DBIC abgerufen und anschließend eine Methode aufgerufen, um zu überprüfen, ob das Kennwort gültig ist.
Sicher, die Bereitstellung von Schemas kann schwierig sein, ist jedoch immer schwierig. DBIC verfügt über Tools (einige in externen Modulen), die es einfacher machen und wahrscheinlich einfacher sind, als Ihre eigenen Schemas manuell zu verwalten.
Auf der anderen Seite der Medaille gibt es andere Tools, die andere Empfindlichkeiten ansprechen, wie die DBI-Wrapper mit Mojo-Geschmack. Diese haben den Reiz, schlank und dennoch nutzbar zu sein. Die meisten haben sich auch an Mojo :: Pg (dem Original) orientiert und nützliche Funktionen wie die Schema-Verwaltung in Flat-Files und die Pubsub-Integration hinzugefügt.
Diese Mojo-Module sind aus einer anderen Schwachstelle von DBIC hervorgegangen, die darin besteht, dass sie (noch) keine asynchronen Abfragen ausführen können. Die Autoren haben mir versichert, dass es technisch vielleicht sogar schnell möglich ist, aber es gibt Probleme beim Entwerfen einer API, die passen würde. (Zugegeben, ich wurde sogar gebeten, mir dabei zu helfen, und obwohl ich das tun würde, weiß ich einfach nicht, wie ich die Nadel in der Zeit bewegen soll, in der ich mich ihr widmen muss).
TL; DR verwenden DBIC, es sei denn, Sie lieben SQL oder Sie benötigen Async. In diesem Fall sollten Sie die Mojo-DBI-Wrapper untersuchen.
quelle
Ich habe meine Gedanken dazu vor drei Jahren in DBIC vs DBI geschrieben. Zusammenfassend habe ich zwei Hauptgründe aufgeführt:
Was Anti-Patterns angeht, kann ich mir nur Performance vorstellen. Wenn Sie wirklich jeden Taktzyklus aus Ihrer CPU herauspressen möchten, ist DBIC möglicherweise nicht das richtige Werkzeug für diesen Job. Aber sicherlich für die Anwendungen, die schreiben, werden diese Fälle immer seltener. Ich kann mich nicht erinnern, wann ich das letzte Mal eine neue Anwendung geschrieben habe, die mit einer Datenbank kommuniziert und DBIC nicht verwendet hat. Natürlich hilft es, wenn Sie ein wenig über das Optimieren der von DBIC generierten Abfragen wissen.
quelle
So wie ich es skaliere:
Erstellen Sie eine Klasse, die den DBI-Socketkonstruktor und die Testmethoden bereitstellt.
Leiten Sie diese Klasse in Ihre SQL-Abfrageklassen ab (eine Klasse pro SQL-Tabelle), und testen Sie sie zur Konstruktorzeit auf den Socket.
Verwenden Sie klassenbezogene Variablen, um Ihren Tabellennamen und die Namen der Primärindexspalten zu speichern.
Schreiben Sie alle Namen der SQL-Interpolationstabelle und die Primärindexspalte aus diesen Variablen, anstatt sie statisch in SQL zu definieren.
Verwenden Sie Editor-Makros, um grundlegende DBI-Methodenpaare zu erstellen (vorbereiten und ausführen), während Sie NUR die SQL-Anweisung eingeben.
Wenn Sie dies tun können, können Sie relativ einfach den ganzen Tag sauberen API-Code auf das DBI schreiben.
Sie werden feststellen, dass viele Ihrer Abfragen auf mehrere Tabellen übertragbar sind. An diesem Punkt können Sie ausschneiden und in eine EXPORTER-Klasse einfügen und sie bei Bedarf wieder einstreuen. Hier kommt die klassenbezogene Interpolation des Tabellennamens und der Namen der Primärindexspalten ins Spiel.
Ich habe diesen Ansatz verwendet, um Hunderte von DBI-Methoden mit relativ guter Leistung zu skalieren. Ich würde nicht versuchen, DBI-Code auf andere Weise zu pflegen.
Wann man das DBI benutzt: Immer.
Ich glaube nicht, dass das deine eigentliche Frage war. Ihre eigentliche Frage war: "Das sieht aus wie eine riesige PITA, bitte sagen Sie mir, dass ich das nicht tun muss."
Das tust du nicht. Ordnen Sie es richtig an und der DBI-Teil wird so redundant, dass er größtenteils automatisiert werden kann.
quelle
Ich bin kein Perl-Experte, aber ich benutze es oft. Es gibt eine Menge Dinge, die ich nicht weiß oder besser machen kann. manche sachen verstehe ich trotz dokumentation einfach noch nicht.
Ich tendiere dazu, mit DBI zu beginnen, weil ich denke: "Oh, das ist ein einfaches Projekt, ich brauche nicht die Aufblähung eines ORM und ich möchte mich nicht mit den Setup- und Schema-Modulen herumärgern." Aber sehr schnell - fast jedes Mal - beginne ich, mich für diese Entscheidung zu verfluchen. Wenn ich anfangen möchte, meine SQL-Abfragen kreativ zu gestalten (dynamische Abfragen, nicht nur Vergleichsplatzhalter), bemühe ich mich, mit DBI vernünftig zu bleiben. SQL :: Abstract hilft mir sehr, und in der Regel reicht mir das wahrscheinlich aus. Aber mein nächster mentaler Kampf besteht darin, so viel SQL in meinem Code zu behalten. Es ist sehr ablenkend für mich, Zeilen und Zeilen von Embedded SQL in hässlichen Heredocs zu haben. Vielleicht muss ich eine hochwertige IDE verwenden.
Am Ende bleibe ich mehrmals bei Straight DBI. Aber ich wünsche mir immer wieder, es gäbe einen besseren Weg. DBIx :: Class hat einige wirklich nette Eigenschaften und ich habe es ein paar Mal benutzt, aber es scheint für alle außer den größten Projekten so übertrieben. Ich bin mir nicht mal sicher, was ich schwieriger zu verwalten finde: DBI mit verstreutem SQL oder DBIC mit verstreuten Schemamodulen.
(Oh, Dinge wie Einschränkungen und Trigger sind für mich ein großes Plus für DBIC.)
quelle