28.02.21 2607 Views Kommentare [] 3 0

credit: ©Stephen Coburn

Java Tutorial #45

Java MySQL Prepared Statement (Tutorial)

Bei der Kommunikation mit MySQL-Datenbanken ist es ratsam, keine einfachen SQL-Statements zu verwenden, sondern auf sogenannten PreparedStatements zu setzen. Das ist nicht nur sicherer, sondern auch besser in Hinsicht auf Performance und Handling.

Falconbyte unterstüzen

Betrieb und Pflege von Falconbyte brauchen viel Zeit und Geld. Um dir auch weiterhin hochwertigen Content anbieten zu können, kannst du uns sehr gerne mit einem kleinen "Trinkgeld" unterstützen.

Schnelles Code-Beispiel:

String sql = "SELECT * FROM personen WHERE name = ? AND vorname = ?";
PreparedStatement ps = con.prepareStatement(sql);

ps.setString(1, "Musk");
ps.setString(2, "Elon");

ResultSet result = ps.executeQuery();

    Inhaltsverzeichnis

  1. Was ist ein PreparedStatement in Java?
  2. Die Vorteile von PreparedStatement im Detail
  3. Übungen

Was ist ein PreparedStatement in Java?

Ein PreparedStatement in Java ist eine besondere Art von Statement. Es erweitert das Interface Statement und hat diesem gegnüber einige Vorteile:

  • Bessere Performance
  • Bessere Lesbarkeit des Codes
  • Wiederverwertbarkeit
  • Mehr Sicherheit

Übersetzt bedeutet PreparedStatement "vorbereitete Anweisung". "Vorbereitet" deshalb, weil die SQL-Anweisung (SELECT, INSERT, UPDATE) in einem ersten Schritt zur Datenbank lediglich geschickt wird, die tatsächliche Ausführung aber zu einem späteren Zeitpunkt erfolgt.

Im Unterschied zum "normalen" Statement funktioniert das PreparedStatement statt mit konkreten Werten mit Platzhaltern (Placeholder), die durch ein Fragezeichen (?) ausgedrückt werden.

So erstellst du ("bereitest vor") ein PreparedStatement:

String sql = "SELECT * FROM personen WHERE id = ?";
PreparedStatement ps = con.prepareStatement(sql);

Nach Erhalt des PreparedStatement wartet die Datenbank auf die Ausführung. Die Ausführung des PreparedStatements erfolgt dann in einem zweiten Schritt, nachdem die fehlenden Werte für die SQL-Anweisung mit speziellen Methoden nachgereicht worden sind.

Platzhalter durch Werte ersetzen

Für jedes Fragezeichen (?) muss im PreparedStatement ein passender Wert eingesetzt werden. Hierfür gibt es spezielle Setter-Methoden:

Die Setter-Methoden von PreparedStatement
setInt(int paramIndex, int value) Setzt einen int-Wert auf den gewählten Platzhalter-Index
setString(int paramIndex, String value) Setzt einen String auf den gewählten Platzhalter-Index
setDouble(int paramIndex, double value) Setzt einen double-Wert auf den gewählten Platzhalter-Index

Die Indices der Platzhalter beginnen bei 1 (nicht wie gewohnt bei 0).

Um also im Beispiel oben den Platzhalter bei id mit einem konkreten Wert zu ersetzen, kann die setInt()-Methode ausgeführt werden:

ps.setInt(1, 7);

Damit wird im PreparedStatement der Platzhalter auf Indexposition 1 mit dem Wert 7 ersetzt.

Man kan auch mehrere Platzhalter einsetzen. Hier ein Beispiel:

String sql = "SELECT * FROM personen WHERE name = ? AND vorname = ?";
PreparedStatement ps = con.prepareStatement(sql);

ps.setString(1, "Musk");
ps.setString(2, "Elon");

PreparedStatement ausführen

Um das PreparedStatement nun endlich auszuführen, gibt es zwei Methoden, nämlich executeQuery() und executeUpdate().

executeQuery() wird für alle SELECT-Abfragen eingesetzt:

String sql = "SELECT * FROM personen WHERE name = ? AND vorname = ?";
PreparedStatement ps = con.prepareStatement(sql);

ps.setString(1, "Musk");
ps.setString(2, "Elon");

ResultSet result = ps.executeQuery();

Wie bei gewöhnlichen Statements auch, wird das Ergebnis in einem Objekt vom Typ ResultSet gespeichert. Wie wir durch das ResultSet iterieren, siehst du im Tutorial zu Java MySQL Daten auslesen.

executeUpdate() dagegen verwendet man für alle SQL-Anweisungen, die den Inhalt von Tabellen manipulieren (INSERT, UPDATE, DELETE):

String sql = "UPDATE personen SET name = ?, vorname = ? WHERE id = ?";
PreparedStatement ps = con.prepareStatement(sql);

ps.setString(1, "Magellan");
ps.setString(2, "Ferdinand");
ps.setInt(3, 123);

int rows = ps.executeUpdate();

Die executeUpdate()-Methode liefert einen int-Wert zurück, der anzeigt, wieviele Datensätze von der Änderung betroffen sind.

Infografik

Das Grundprinzip hinter einem PreparedStatement in Java siehst du zusammengefasst in folgender Infografik:

Java PreparedStatement Grafik

Die Vorteile vom PreparedStatement im Detail

Wie gesagt, solltest du in einem Produktivsystem auf PreparedStatements setzen. Wir zeigen dir im Folgenden die Gründe dafür:

Bessere Performance

Einen Datenbank benötigt immer eine gewisse Zeit, um einen SQL-String zu verarbeiten: Zuerst wird der SQL-String zur Datenbank geschickt. Danach wird er dort auf syntaktische Gültigkeit geprüft. Anschließend wird ein interner Ausführungsplan (Query plan) erzeugt, der dazu dient, die Ausführung der SQL-Abfrage möglichst effizient durchzuführen.

Jedes mal, wenn ein neues Statement an die Datenbank geschickt wird, wird dieser Vorgang wiederholt. Die Arbeitszeit, die die dafür benötigt wird, ist messbar.

Indem wir aber ein PreparedStatement mit seiner Vorübersetzung einsetzen, müssen sowohl die Syntax-Prüfung des SQL-Strings als auch das Aufstellen des internen Ausführungsplans nur ein einziges Mal durchgeführt werden.

Mit einem PreparedStatement gewinnen wir also gerade vielen Abfragen (Schleifen!) signifikante Performance-Vorteile!

Bessere Lesbarkeit des Codess

Ein PreparedStatement sorgt für deutlich leichter zu lesenden Code.

Sieh dir zunächst einmal dieses "normale" Statement an:

int id = 100;
String kategorie = "Stahl";

String sql = "SELECT * FROM kunden
             "WHERE id > " + id + " AND kategorie = '" + kategorie + "';";

Statement stm = con.createStatement();
ResultSet rs = stm.executeQuery(sql);

Ganz klar: Durch die String-Verkettung und die Ausrufezeichen (einfache und doppelte) wirkt das Ganze recht unübersichtlich. Das Schreiben solcher Strings ist auch nicht immer eine Freude. Je mehr Variablen im Spiel sind, desto grausamer wird das Ganze.

Besser ist das Ganze mit einem PreparedStatement:

String sql = "SELECT * FROM kunden WHERE id > ? AND kategorie = ?;";

PreparedStatement ps = con.prepareStatement(sql);

ps.setInt(1, 100);
ps.setString(2, "Stahl");

ResultSet result = ps.executeQuery();

Wiederverwertbarkeit

Nachdem ein PreparedStatement erst einmal vorbereitet worden ist, kann es sehr einfach nach der Ausführung wiederverwendet werden. Wir müssen einfach nur die Werte der setter-Methoden ändern. Hier ein Beispiel:

String sql = "SELECT * FROM kunden WHERE id > ? AND kategorie = ?;";
PreparedStatement ps = con.prepareStatement(sql);

ps.setInt(1, 100);
ps.setString(2, "Stahl");
ResultSet result = ps.executeQuery();

ps.setInt(1, 50);
ps.setString(2, "Blech");
ResultSet result2 = ps.executeQuery();

Mehr Sicherheit

Nicht zuletzt erhöhen PreparedStatements die Sicherheit, indem Sie sog. "SQL-Injections" verhindern können. Bei SQL-Injections handelt es sich um einen Missbrauch von SQL-Abfragen.

Hier ein Beispiel:

public void del(String name) throws SQLException{
    String sql = "DELETE FROM kunden WHERE name = '" + name + "';";
    Statement stm = con.createStatement();
    stm.execute(sql);
}

Diese Methode nimmt einen String-Parameter an setzt daraus den SQL-DELETE-String zusammen. Der passende Datensatz wird gelöscht. Was geschieht aber, wenn der Methode statt eines gültigen Namens "EGAL' OR 1 = 1 OR name='EGAL" als Argument übergeben wird, der SQL-String also so zusammengesetzt wird:

DELETE FROM kunden WHERE name = 'EGAL' OR 1 = 1 OR name='EGAL';   

In diesem Fall würden alle Datenätze gelöscht werden! Die eingeschleuste (injected) ODER-Verknüpfung lässt die WHERE-Prüfung nämlich für alle Zeilen wahr werden.

Glücklicherweise gibt es unser PreparedStatement. Damit ist ein solches "Hacking" nicht mehr möglich:

String sql = "DELETE FROM kunden WHERE name = ?;";
PreparedStatement ps = con.prepareStatement(sql);
ps.setString(1, name);
ps.execute();

Besonders bei grafischen Benutzeroberflächen (GUIs), wo der Benutzer Eingabefelder zur Datenbank-Abfrage vorfindet, sollte unbedingt mit PreparedStatements gearbeitet werden. Sonst wäre es ein Leichtes für Unbefugte, die Datenbank zu hacken und Kundendaten auszulesen, zu manipulieren oder zu löschen.

Übungen

einfach

Warum solltest du in Produktivsystem auf PreparedStatements setzen?

Lösung ein-/ausblenden

mittel

Wandle das folgende normale SQL-Statement in ein PreparedStatement um. Vergebe dann die Werte für die Platzhalter und führe das PreparedStatement aus:

String name = "Simba";
int id = 2;

String update = "UPDATE haustiere SET name=" + name + " WHERE id=" + id + ";";
Statement stm = con.createStatement();
stm.execute(update);
Lösung ein-/ausblenden

schwer

Wieviele Fehler befinden sich im folgenden Code (in welchen Zeilen)? Wodurch werden sie ausgelöst?

try {
String sql = "UPDATE stern SET name = ?, alterInMrd = ? WHERE id <= ?";
PreparedStatement ps = con.prepareStatement(sql);

ps.setString(1, "Sonne");
ps.setString(2, 4); //
ps.setInt(3,6);

ps.executeQuery(); //

}
catch(Exception e){
    System.out.println(e);
}
}
Lösung ein-/ausblenden

Java Variablen erklärt

Wie werden Variablen eingesetzt? Was bedeutet Deklarieren und Initialisieren?

Arrays in Java

Arrays ermöglichen das Speichern von Daten in einer übergeordneten Datenstruktur

Verzweigungen in Java

Eine zentrale Notwendigkeit der Programmierung sind Verzweigungen.

FALCONBYTE.NET

Handmade with 🖤️

© 2018-2022 Stefan E. Heller

Impressum | Datenschutz | Changelog

Falconbyte GitHub facebook programmieren lernen twitter programmieren lernen discord programmieren lernen