Wenn man mit Firebird-Datenbanken arbeitet, kommt irgendwann der Punkt, an dem man genau wissen möchte, welche SQL-Statements tatsächlich auf der Datenbank ausgeführt werden. Vielleicht, um Performance-Probleme zu untersuchen, vielleicht um unerwartetes Verhalten einer Anwendung zu verstehen oder einfach, um ein besseres Gefühl für den Datenbankzugriff zu bekommen.
Firebird bringt dafür seit der Version 2.5 ein sehr mächtiges Werkzeug mit: das Trace- und Audit-Subsystem. Damit lassen sich Abfragen, Transaktionen, Verbindungsinformationen und vieles mehr protokollieren – ohne dass man tief ins System eingreifen oder die Anwendung verändern muss.
In diesem Beitrag zeige ich dir Schritt für Schritt, wie du mit Firebird 2.5.9 unter Linux (egal ob direkt auf dem Host oder in einem Docker-Container) ein SQL-Logging aktivierst und die Ergebnisse live einsehen kannst.
Trace-Konfigurationsdatei erstellen
Der Trace-Mechanismus in Firebird braucht zunächst eine Konfigurationsdatei, die beschreibt, was geloggt werden soll. Diese Datei kann an einem beliebigen Ort liegen – wir nehmen in diesem Beispiel /opt/fb_trace_all.conf.
Öffne die Datei mit einem Editor deiner Wahl, zum Beispiel nano oder vi:
* enabled true – Schaltet das Logging für die Datenbank ein.
* log_connections true – Verbindungs- und Trennungsereignisse werden protokolliert.
* log_statement_prepare true – Auch das „Vorbereiten“ eines SQL-Statements (also bevor es ausgeführt wird) wird festgehalten.
* log_statement_start true / log_statement_finish true – Beginn und Ende jeder Abfrage werden geloggt.
* print_plan true – Der Ausführungsplan (Execution Plan) für jede Abfrage wird mit ausgegeben – sehr nützlich, wenn es um Performance geht.
* print_perf true – Zeitmessungen und andere Performance-Daten werden angezeigt.
* time_threshold 0 – Es gibt keine Mindestzeit; jede Abfrage wird geloggt, egal wie kurz sie läuft.
* max_sql_length / max_arg_length / max_arg_count – Diese Werte legen fest, wie groß die protokollierten SQL-Statements und ihre Parameter werden dürfen.
Mit dieser Konfiguration erhältst du ein sehr detailliertes Protokoll, das alle SQL-Statements sichtbar macht, die gegen deine Firebird-Datenbank laufen.
Trace Watcher starten
Nun muss Firebird angewiesen werden, diesen Trace-Job zu starten. Das geschieht über das Tool fbtracemgr, das mit der Firebird-Installation mitgeliefert wird.
-se localhost:service_mgr – Verbindung zum Service Manager auf localhost.
-user SYSDBA -pass masterkey – Authentifizierung mit dem Standard-Administrator-Account.
-start – Startet einen neuen Trace-Job.
-name allsql – Vergibt einen Namen für diesen Job, z. B. „allsql“.
-conf /opt/fb_trace_all.conf – Verweist auf die Konfigurationsdatei von oben.
>> /tmp/lognamen.log – Leitet die Ausgabe in eine Log-Datei um.
Der Prozess läuft so lange, bis er manuell beendet wird (z. B. mit Ctrl+C).
Logdatei live mitverfolgen
Um nicht immer wieder die Log-Datei neu öffnen zu müssen, empfiehlt sich ein „Live-Tail“. In einem zweiten Terminal kannst du dir die Einträge direkt beim Eintreffen anschauen:
tail -f /tmp/lognamen.log
Jetzt siehst du sofort, wenn deine Anwendung eine Abfrage an Firebird schickt – inklusive der SQLs, der Ausführungspläne und der Performance-Daten.
Praktische Tipps Docker-Setup: Wenn Firebird in einem Container läuft, musst du nur darauf achten, dass sowohl das fbtracemgr-Binary als auch die Konfigurationsdatei im Container verfügbar sind. Ein Volume-Mount für /opt/fb_trace_all.conf und /tmp/lognamen.log ist sinnvoll.
Ressourcen im Blick behalten: Da jede Abfrage geloggt wird, kann die Logdatei schnell sehr groß werden. Für längere Analysen solltest du mit Filtern arbeiten oder ein Tool wie grep verwenden.
Filterung: Du kannst in der Konfigurationsdatei auch bestimmte Datenbanken oder bestimmte Events ausschließen, um die Menge der Informationen überschaubarer zu halten.
Sicherheit: Achte darauf, dass sensible Daten (Passwörter, personenbezogene Daten) in Logs landen können. Im Produktivbetrieb sollte man also genau abwägen, welche Daten man wirklich braucht.
Fazit
Mit dem Firebird-Trace-Subsystem hat man ein mächtiges Werkzeug an der Hand, das es erlaubt, SQL-Abfragen transparent zu überwachen und zu analysieren. Gerade bei der Fehlersuche oder bei Performance-Optimierungen ist das oft der schnellste Weg, um zu verstehen, was im Hintergrund tatsächlich passiert.
Die hier gezeigte Konfiguration ist ein „Alles mitnehmen“-Ansatz, mit dem man sofort loslegen kann. Danach kann man Schritt für Schritt Feintuning betreiben, um genau die Informationen zu loggen, die man wirklich braucht.
Einmal schnell gucken, wie viele und welche Art von Produkten über welchen SalesChannel und URL zu kaufen sind.
SELECT x.url, 'single' type, count(x.id)
FROM (
select d.url, p.id, p.parent_id, count(p2.id) children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
group by d.url, p.id, p.parent_id
)x
WHERE x.parent_id is null and x.children < 1
group by x.url
union all
SELECT x.url, 'main' type, count(x.id)
FROM (
select d.url, p.id, p.parent_id, count(p2.id) children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
group by d.url, p.id, p.parent_id
)x
WHERE x.parent_id is null and x.children > 0
group by x.url
union all
SELECT x.url, 'variant' type, count(x.id)
FROM (
select d.url, p2.id, p2.parent_id, 0 children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
)x
group by x.url
Mal wieder kam eine Bestellbestätigung von HD+ an. Selbes Schema, wie das letzte mal. Wirklich toll ist, dass die letzten Male sogar eine Email mit dem SEPA-Mandat mitgeschickt wurde, weil damit lässt sich sehr genau sagen, welche Daten von einem von jemanden Missbraucht werden und welche nicht. Daher kann ich jetzt schon mal sagen, dass jemand wohl regelmäßig meine Email-Adresse für Bestellungen verwendet, aber immer mit anderen Bankdaten kombiniert. Das ist an sich nicht sehr klug von dieser Person, da so Unregelmäßigkeiten schnell zu finden sein sollten. Eine Email-Adresse mit verschiedenen Kontodaten sollte sich per SQL schnell finden lassen.
Diesmal habe ich eine Email geschrieben und auch, um die Rücknahme der Bestellung gebeten. Mal gucken wie die reagieren werden.
Die Kontodaten stammen diesmal von der VR GenoBank DonauWald eG... also irgendwo aus Bayern.
Edit: HD+ hat mir geraten bei der Polizei Anzeige gegen Unbekannt zu erstatten.
Viele Probleme lassen sich nicht durch einfache SQL-Queries lösen. Gerade wenn es um Daten-Generierung und Dinge geht, wo man die Logik nicht in allen Clients umsetzen möchte, die auch die Datenbank zugreifen.
Wärend Stored Procedures coole Dinge wie IF, WHILE und so können, haben die doch oft einen schlechten Ruf, weil man dann ja nicht mehr Datenbank unabhängig wäre, wenn man anfängt diese zu verwenden. Aber am Ende wird man sowie so nie wirklich die Datenbank wechseln und sollte man das tun, dann eher von SQL zu NoSQL und da ist es dann auch egal, da man alles alle neu anpassen muss.
Eine einfache Procdure in MySQL:
DELIMITER \\
DROP PROCEDURE IF EXISTS exampleProcedure\\
CREATE PROCEDURE exampleProcedure()
BEGIN
DECLARE checkval INT;
SET expectedval = 0;
START TRANSACTION;
-- implement your logic here
IF checkval = expectedval THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END \\
DELIMITER ;
CALL exampleProcedure();
DROP PROCEDURE IF EXISTS exampleProcedure;
Es gibt auch die klassischen IN und OUT Parameter. Alles ganz einfach und schnell zu schreiben. Sieht ein wenig aus wie Pascal. ABER im Vergleich zu PL/SQL fehlen doch eine Datentypen wie RECORDs, um komplexere Datenstrukturen anlegen zu können oder direkter mit Tables arbeiten zu können. SELECT .. INTO ist nett, aber RECORDs machen es doch sehr viel einfacher und strukturierter.
Aber an sich kommt man damit zurecht und man sollte doch mehr davon und auch öfters Stored Procedures verwenden, um sich in einigen Teilen das Leben etwas leichter zu machen.
Ich habe PL/SQL immer gerne verwendet, auch wenn ich oft genug daran verzweifelt bin.
Vor einiger Zeit hatte ich eine PLZ-Suche implementiert, bei der Locations bis zu einer bestimmten Distanz zu einer 2. per PLZ identifizierten Location als Ergebnismenge heraus gesucht wurden.
Es wurde also Also eine Distanz zwischen 2 per GPS-Koordinaten beschrieben Locations berechnet. Ich habe das in PHP erledigt, weil die Locations der zu findenen Orte in einer JSON-Datei vorlagen und ich mir den Import in die MySQL-DB ersparen wollte. Die Orte per PLZ kamen aber aus der MySQL-DB, wobei immer der erste Ort mit der PLZ die Koordinaten geliefert hat.
Auf der Seite findet man auch Code für PL/SQL (Oracle), Java und JavaScript. Nur leider für MySQL nicht. Die findet man aber hier. Im Grunde kann man sich das aber für jede Sprache selbst ableiten, weil es normale Mathematische Berechnungen sind ohne dass zusätzliche Libs oder Klassen nötig wären (die es aber auch gibt und diese Logik kapseln).
Jeden Falls sind solche Berechnungen an sich ganz einfach und performant, so dass man sich alles schnell selbst schreiben kann und nicht auf 3rd Party Lösungen angewiesen ist.
Die Differenz zwischen zwei Datumswerten in Tagen:
Java (LocalDateTime)
if(ldt1.isAfter(ldt2)){
days = Duration.between(ldt1.toLocalDate().atStartOfDay(), ldt2.toLocalDate().atStartOfDay()).toDays();
days = Math.abs(days);
}
Java (Date)
long days = TimeUnit.DAYS.convert(date.getTime() - date2.getTime(), TimeUnit.MILLISECONDS);
Das Arbeiten mit der Time-API von Java ist teilweise echt nicht einfach. Auch gibt es vielmehr zu schreiben und ohne Hilfe aus dem Internet geht es einfach nicht. Wobei ich die ".from()"-Methoden schon wirklich nett finde.
Wenn man die letzten 2-3 Jahre fast nur PHP gemacht hat, erschlägt einen es fast schon, von der Fülle an Kombinationen und Klassen die man hier benötigt. Jeden Falls auf den ersten Blick. Auf den zweiten sieht es besser aus und auf den dritten gefällt es einen dann auch schon wirklich.
Und im Gegensatz zum guten alten Date mit SimpleDateFormat funktioniert es auch ohne Probleme.
Java tut auch hier was es am Besten kann: Es zwingt einen es richtig zu machen.. mit ZoneId und allem was man sonst der Bequemlichkeit halber oft lieber weg lässt.
"Ich habe mit Oracle-DB gearbeitet und musste dort Konstrukte drin bauen, die einen 2 Tage Arbeitszeit und tausende Nerven gekostet haben und man 4 mal pro Stunde an sich selbst gezweifelt hat. Manchmal war einem am Ende nicht mal ganz klar, warum es nun wirklich doch funktionierte.
Jetzt arbeite ich mit MySQL/MariaDB... und wünsche mir oft die Oracle-DB zurück, weil ich damit diese Konstrukte überhaupt bauen konnte!"
Mein Kommentar dazu, wenn mal wieder über Oracle und deren doofe DB geweint wird.
Bevor es dann wirklich mal um die Installation von PDT in Eclipse geht, wollen wir uns erst einmal eine MySQL Datenbank einrichten, damit wir auch richtige kleine Anwendungen schreiben können und nur ganz selten kommt man da ohne Datenbank aus. Auch wenn NoSQL Datenbanken wie Neo4J wirklich toll und momentan sehr in sind, bleiben wir bei einem klassischen RDBMS. Weil MySQL gerade im Web sehr verbreitet ist und sich einfach lokal einrichten lässt, bleiben wir auch bei MySQL.
Wer ein Linux benutzt, kann MySQL immer ganz einfach über den Paket-Mananger installieren. Für Windows kann man schnell zu XAMPP greifen. XAMPP enthält alles vom Apache, PHP7 und MySQL bzw MariaDB.
XAMPP funktioniert am Besten wenn man es direkt unter C:\ installiert.
Zum Verwalten der Server-Anwendungen von XAMPP gibt es das XAMPP Control Panel. Hier müssen nur der Apache und der MySQL Server gestartet
werden.
Damit auf die Datenbank zugegriffen werden kann bringt XAMPP phpMyAdmin mit. phpMyAdmin ist eine Datenbankverwaltung die in PHP geschrieben ist und auch von den meisten Hostern angeboten wird.. wenn nicht sogar von allen. Man kann direkt über die URL http://localhost/phpmyadmin darauf zugreifen.
Zuerst erstellen wir eine Datenbank mit dem Namen blog_test.
Damit wir ein paar Daten haben, legen wir uns eine Tabelle mit ein paar wenigen Daten an. Hier ist das SQL-Script dafür:
CREATE TABLE TEST_ITEMS(
ITEM_ID INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
ITEM_VALUE VARCHAR(255) NOT NULL,
PRIMARY KEY(ITEM_ID)
);
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_01');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_02');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_03');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_04');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_05');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_06');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_07');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_08');
INSERT INTO TEST_ITEMS (ITEM_VALUE) VALUES('TEST_09');
Die IDs werden automatisch hochgezählt und müssen deswegen nicht extra angegeben werden.
Nun gehen wir wieder in Eclipse zurück und erstellen uns eine kleine JSP mit einer Datenbankabfrage. Und hier wird es etwas komplizierter mit den verschiedenen ClassLoader des Tomcats und den verschiedenen Config-Dateien. Ich hab die einfachste aber nicht beste Variante gewählt. Die JAR-Datei mit dem JDBC-Treiber kommt direkt in das lib-Verzeichnis des Tomcat und wir passen die zentrale context.xml Datei, die uns von Eclipse zur Verfügung gestellt wird.
Die aktuelle JAR mit dem MySQL-Treiber findet man auf dev.mysql.com.
In die Context-Datei definierten wir die DataSource als Resource.
Nun können wir über JNDI uns diese DataSource in eine JSP holen. Wir erstellen eine ganz einfach Abfrage. Das Ergebnis liefert uns ein Statement in Form eines ResultSets. Ich benutzt hier die Methode über den Index die Ergebnisse zu bekommen, z.B. getString(1) wobei aber auch getString("ITEM_ID") funktioniert und für den zielgerichteten Einsatz sehr viel besser ist, weil man so das SQL-Statement ändern kann und auch die Reihenfolge der Columns ändern, ohne dabei auf den Java-Code achten zu müssen. Hier wird aber nicht zielgerichtet ein Wert ausgelesen und z.B. in ein anderes Object geschrieben sondern einfach alles ausgegeben. Deswegen auch nur getString() und keine anderen Methoden, die einen passenden Datentyp zurück liefern und ein eigenes Casten der Werte unnötig machen.
Der Vorteil die Connection über eine DataSource zu bekommen und nicht jedes mal selbst zu initiieren ist, dass die DataSource ein Pooling der Connections vornimmt und Datenbankverbindungen zur Wiederverwendung offen hält, um den Overhead für Verbindungsaufbauten zu verringern.
SQL-Abfragen direkt in einer JSP-Seite zu ist aber eine schlechte und man sollte so etwas in DAO-Klassen auslagern und in der JSP nur die Ansicht mit schon fertigen Objekten erstellen, die dann vom DAO geliefert werden.
Außerdem werden immer mehr JPA verwendet, wo die SQL-Statements automatisch erzeugt werden. Handgeschriebenes SQL ist in komplexen Fällen meistens schneller und besser, aber ORM-Frameworks erleichtern einen die Arbeit schon sehr und man sollte sich JPA auf jeden Fall einmal
ansehen, bevor man noch direkt mit JDBC und SQL arbeitet.
Im nächsten Teil geht es dann wirklich mit PHP weiter.
Als ich mit PHP anfing war es noch Alltag, dass man wenn eine Datenbank-Abfrage notwendig war diese auch einfach direkt an Ort und Stelle einbaute. Kapselung in einer eigenen Funktion oder Klasse war nicht wirklich verbreitet. Es wurde meistens direkt mit den entsprechenden Datenbank Funktionen gearbeitet und keine Abstraktion verwendet.
Nur in großen Anwendungen war es anders. Vorher hatte ich mit Java, JDBC und DataSources im Tomcat gearbeitet, wo es am Ende egal war welche Datenbank dahinter lief und man dies nur beim Anlegen der DataSource abgeben musste. Zuhause war ich auf Oracle Datenbanken und hatte nur so am Rande mit MySQL zu tun. Ich sollte ein Projekt von MySQL auf Oracle portieren und merkte schnell wie doof es war keine Abstraktion zu haben. Also nahm ich mir vor, sollte ich mal ein PHP anfangen, dass ich es besser machen würde. Genau so einfach wie mit einem Tomcat und den DataSources.
Dann kam in der Berufsschule das Thema Datenbanken, dass wir mit einer XAMPP-Installation bearbeiten sollten. Ein kleines Shop-"System". CRUD, eine Liste laden und wenn wir voll gut waren sogar Joins oder mal ein COUNT mit GROUP BY.. also alles was die meistens aus ihrem normalen Arbeitstag in und auswendig kannten. Ich wollte es dann auch gerne so schreiben, dass ich SQL über eine zentrale Klasse ausführen konnte und Schleifen einfach ausführen konnte und das Problem das bei Oracle ein Array so aussah [column]
und nicht wie bei MySQL
[column] direkt dort in der Implementierung ausgeglichen wurde und man so einfach zwischen den beiden Systemen switchen kontne ohne Code ändern zu müssen.
Aber fangen wir mal an. Zuerst erstellen wir uns eine einfache Datenbank Tabelle mit ein paar Daten. Diese werden dann im Verlaufe dieses Artikels mit PDBC aus der Datenbank laden und einmal ausgeben. Unsere Tabelle sieht wie folgt aus:
CREATE TABLE tests (
test_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
test_name VARCHAR(255) NOT NULL,
PRIMARY KEY (test_id)
);
INSERT INTO tests(test_name) VALUES ('TEST_01');
INSERT INTO tests(test_name) VALUES ('TEST_02');
INSERT INTO tests(test_name) VALUES ('TEST_03');
INSERT INTO tests(test_name) VALUES ('TEST_04');
INSERT INTO tests(test_name) VALUES ('TEST_05');
Diese Tabelle repräsentiert Daten für eine einfache Klasse mit einer Id und einem Namen. Also das Grundmodel auf dem bestimmt fast 98% aller Entitäten basieren. Das normale vorgehen wäre jetzt diese Daten über ein einfaches SQL Query einzulesen. Dabei enthält man ein ResultSet oder Array mit allen Zeilen, die man dann in einer Schleife durchläuft. Während jedem Durchlauf erzeugt man ein neues Objekt der Klasse und befühlt dieses über die Setter mit den Daten aus dem Zeilen Array.
$result=array();
foreach($data as $row){
$obj=new Test();
$obj->setId($row["test_id"]);
$obj->setName($row["test_name"]);
$result[]=$obj;
}
Man muss viel per Hand schreiben, was sehr zeitintensiv, stupide und fehleranfällig ist. Wenn man an mehreren Stellen so lädt z.B. einmal laden eines Objekt anhand der Id, einmal die Liste und noch die Liste mit einem Pattern für den Namen, muss man alle Methoden und Funktionen wo man die Daten in das Objekt füllt ändern, wenn man mal eine Column der Datenbank hinzufügt, ändert oder entfernt.
Wenn man diesen Teil nun automatisiert, erleichtert einen das Leben schon sehr. Bei einer Klasse mit 10 Attributen sind es dann nicht mehr 12 Zeilen Code sondern nur noch 2. PDBC erledigt für einen diese Schritte. Vom erzeugen der Objekt, die Abfrage der Daten und dann das befüllen der Objekte.
Um nun zu wissen, welches Datenfeld aus der Datenbank in welches Klassen-Attribute gehört werden Annotationen genutzt. Es ist ganz leicht JPA und Hibernate beeinflusst. Wobei PDBC sehr viel weniger kann.
FKs werden nicht in Objekte abgebildet, also ein lazy oder eager Loading. Es werden nur wirklich Daten kopiert. Das SQL muss man komplett per Hand schreiben, was ich aber als Vorteil sehe, weil HQL/JPQL doch mich immer sehr eingeschränkt haben und viele wichtige SQL-Funktionen wie Nested-Queries und gute Joins oft nicht möglich waren. SQL bietet dort sehr viel mehr und performantere Möglichkeiten.
Aber hier geht es erst einmal um einfaches und schnelles Laden der Datensätze.
Dafür brauchen wir erst einmal unsere Klasse mit den entsprechenden Annotationen, die bestimmen zu welcher Column das Klassen-Attribute gehört. Das Schlüsselwort hierbei ist "@dbcolumn". Diese Annotationen werden mit Hilfe der Refelection Klassen von PHP und den DocComments gelesen und dann geparst.
Damit man hier performant bleibt werden die Properties einmal pro Klasse eingelesen und in einer Map gespeichert, so was man schnell über den Column-Name zum Property gelangt. Wenn man dann für die nächste Klasse die Zuordnung braucht hat man diese schon. Da die Map static ist, geschieht das Einlesen auch nur einmal pro Request für eine Klasse.
setAccessible(true) ist hier sehr wichtig, weil wir so private Attribute lesen und schreiben können und dazu bringt es noch einem wirklich spürbaren Performance-Vorteil. Ja.. auch bei PHP. Bei Java gilt genau das Selbe. Die Reflection-Klassen von PHP sind von den Bezeichnungen und Methoden auch sehr an Java angelehnt und wer mit den Relfections in Java sich auskennt, kann sofort in PHP weiter machen, da die wichtigen Methodennamen wirklich zu 100% übereinstimmen.
public function getId(){
return $this->id;
}
public function setId($id){
$this->id=$id;
}
public function getName(){
return $this->name;
}
public function setName($name){
$this->name=$name;
}
}
Man sieht hier die Annotationen an den Attributen der Klasse. Die Werte werden direkt in die Attribute geschrieben. Die Nutzung der Getter und Setter ist hier nicht implementiert. Wobei das große Vorteile hätte da man so z.B. eine "1" aus der Datenbank auf ein "true" mappen könnte. Dafür werde ich mal eine Implementierung mit eignen Properties schreiben, die zusammen mit dem Property auch die Accessor-Methoden mitliefert, wenn diese vorhanden sind.
Jetzt geht es aber weiter mit dem konkreten Beispiel. Zuerst werden wir uns unsere Datenbank-Klasse holen. Dafür muss die Datenbankverbindung in der Config-Datei angegeben sein. Der Name muss eindeutig sein und mit dessen Hilfe wird die Datenquelle auch identifiziert. Die Bezeichnungen in der Config-Datei orientieren sich Oracle, so nennt sich ein Feld "SID", also Service-ID, das entspricht bei MySQL dem Datenbanknamen. Sollte hier der Standard-Port von MySQL verwendet werden, ist dieser normaler weise nicht anzugeben.
Wir haben hier also unsere Datasource (wie im Tomcat) unter dem Namen "ds_test", die auch die lokale MySQL-Instanz und die Datenbank verweist. Wie man am Passwort schon vermuten kann, habe ich bei mir einfach den Bitnami WAMP-Stack verwendet, aber XAMPP oder eine vollständige Installation gehen natürlich genau so gut.
Wir müssen noch angeben wo die Config-Datei und wo die Datenbank-Klassen zu finden sind.
Wenn man PDBC in sein System integrieren will, kann man so die Config-Datei einfach mit im Config-Verzeichnis des Systems unterbringen.
Nun folgt ein kleines und einfaches SQL-Statement:
$sql="SELECT test_id,test_name FROM tests ORDER BY test_name ASC";
Und nun beginnt der spannende Teil, wir übergeben unser Datebank-Object, den SQL-String und den Klassennamen an den PDBCObjectMapper. Dieser nutzt das Interface der DB-Klasse, weswegen ihm egal ist welche Implementation verwendet wird. Dann wird die Klasse analysiert und deren Properties eingelesen und die passenden Spalten wie schon weiter oben beschrieben eingelesen. Der Rest ist eigentlich relativ primitiv. Erst wird eine Instanz der Klasse erzeugt und dann das Array mit dem Datensatz des Resultsets wir mit $key (Spaltenname) und $value (Wert aus der DB) durchlaufen.
Wird ein Property zu dem $key gefunden wird in dieses das $value geschrieben.
private function fillObject($row,$ref){
$obj=$ref->newInstance();
$props=$this->loadProperties($ref);
foreach($row as $key => $value){
if(isset($props[$key])){
$props[$key]->setValue($obj,$value);
}
}
return $obj;
}
public function queryList($db,$sql,$className){
$db->executeQuery($sql);
$ref=new ReflectionClass($className);
$result=array();
for($i=0;$i<$db->getCount();$i++){
$result[count($result)]=$this->fillObject($db->getRow($i),$ref);
}
return $result;
}
Die loadProperties-Methode habe ich ja weiter oben schon beschrieben. Hier wieder sich daran erinnern, dass wir setAccessible(true) für jedes Properties gesetzt haben und somit ohne Probleme in private-Properties schreiben können.
"Test" ist unser Klassename, $sql unser String mit dem Query-Statement und $db unsere oben in der config-Datei definierten Datasource.
Als Rückgabe erhalten wir, wie alle schon sicher richtig vermutet haben, ein Array mit unseren gefüllten Test-Objekten. Um zusehen, dass alles richtig befüllt wurde, geben wir diese einfach mal aus.
foreach($tests as $test){
echo $test->getId()." - ".$test->getName()."<br>\n";
}
Der gesamte Code für unser kleines Test-Script sieht dann also so aus:
Ich hoffe ich konnte einen kleinen Einblick geben, wie man sich die Arbeit mit Datenbanken in PHP einfacher machen kann und einen Teil der Konzepte aus der Java-Welt in PHP implementieren kann. Man kann natürlich noch sehr viel mehr implementieren und ein kompletes und komplexes ORM-System hier draus bauen, aber das würde hier zu weit führen und ich brauchte es auch so erst einmal nicht. Auch Inserts und Updates darüber zu realisieren und Getter und Setter benutzen zu können wäre toll und steht noch auf dem Plan, aber wohl alles erst im Laufe von 2016.
Ein wenig besinnliches zu Weihnachten. Performance-Analyse auf Oracle Datenbanken. Wenn man heraus finden will welche SQLs viel Zeit verbrauchen, kann man das Query hier verwenden. Es ist ein erster Ansatz und keine absolute Lösung. Aber wenn man Probleme hat, ist es ein guter Einstieg in die Analyse.
select ROUND(disk_reads/decode(executions,0,1,executions)/300) EXTIME, SQL_TEXT
from v$sql
where disk_reads/decode(executions,0,1,executions)/300 >1
and executions>0
order by (disk_reads/decode(executions,0,1,executions)/300) DESC
Oft hilft es am Ende einfach einen Index zu setzen. Aber manchmal muss man tiefer in die Materie gehen.
Blog-entries by search-pattern/Tags:
Möchtest Du AdSense-Werbung erlauben und mir damit helfen die laufenden Kosten des Blogs tragen zu können?